Changeset 35296


Ignore:
Timestamp:
2021-08-16T16:27:24+12:00 (3 years ago)
Author:
davidb
Message:

Improve page layout and styling

Location:
main/trunk/model-interfaces-dev/atea
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/model-interfaces-dev/atea/js/asr/asr-controller.js

    r35294 r35296  
    6060        {
    6161            return {
    62                 files: undefined,
     62                files: [],
    6363                canTranscribe: false,
    6464                isTranscribing: false
     65            }
     66        },
     67        computed:
     68        {
     69            anyFiles() {
     70                return this.files.length > 0;
     71            },
     72            getFileNameList()
     73            {
     74                let fileNameList = "";
     75
     76                for (let file of this.files) {
     77                    fileNameList += file.name + ", ";
     78                }
     79
     80                return fileNameList.slice(0, fileNameList.length - 2);
    6581            }
    6682        },
     
    7793
    7894                if (audioFileInput.files?.length != undefined && audioFileInput.files?.length > 0) {
    79                     this.canTranscribe = true;
    8095                    this.files = audioFileInput?.files
    8196                }
    8297                else {
    83                     this.canTranscribe = false;
    84                     this.files = undefined;
     98                    this.files = [];
    8599                }
    86100            },
     
    104118            /** @type {Map<String, TranscriptionModel>} */
    105119            transcriptions: new Map(),
    106             /** @type {TranscriptionError[]} */
    107             failures: [], // TODO: Ability to remove failures
    108             showCharDisplay: false
     120            /** @type {Map<String, TranscriptionError>} */
     121            failures: new Map(),
     122            showCharDisplay: false,
     123            currentAudioTime: 0,
     124            audioDuration: 0,
     125            showWordList: false
    109126        }
    110127    },
     
    147164            console.log("Starting at " + startTime + " seconds");
    148165            loadAudioFile(fileName);
     166            this.currentAudioTime = startTime;
    149167            TRANSCRIPTION_AUDIO_ELEMENT.currentTime = startTime;
    150168            TRANSCRIPTION_AUDIO_ELEMENT.play();
     
    153171            this.transcriptions.delete(id);
    154172            // TODO: delete cached audio file
     173        },
     174        removeFailure(id) {
     175            this.failures.delete(id);
    155176        },
    156177        getWords(transcriptionId)
     
    203224
    204225            return chars;
     226        },
     227        toggleWordList() {
     228            this.showWordList = !this.showWordList;
    205229        }
    206230    }
     
    254278            {
    255279                if (!t.success) {
    256                     TranscriptionsListVM.failures.push(new TranscriptionError(t.log, t.file_name));
     280                    TranscriptionsListVM.failures.set(UUID.generate(), new TranscriptionError(t.log, t.file_name));
    257281                }
    258282                else {
     
    356380}
    357381
    358 /**
    359  * Recurses through the entire tree of a parent and adds the given data attribute.
    360  *
    361  * @param {Element} parent The parent node.
    362  * @param {String} dataElementName The name of the data element. Must use hyphen notation, rather than camel case.
    363  * @param {String} value The value of the data element.
    364  */
    365 function recurseAddData(parent, dataElementName, value)
    366 {
    367     parent.setAttribute("data-" + dataElementName, value);
    368 
    369     for (const child of parent.children) {
    370         recurseAddData(child, dataElementName, value);
    371     }
    372 }
     382TRANSCRIPTION_AUDIO_ELEMENT.addEventListener('durationchange', function() {
     383    TranscriptionsListVM.audioDuration = TRANSCRIPTION_AUDIO_ELEMENT.duration;
     384});
     385
     386TRANSCRIPTION_AUDIO_ELEMENT.addEventListener('timeupdate', function() {
     387    console.log("time updated");
     388    TranscriptionsListVM.currentAudioTime = TRANSCRIPTION_AUDIO_ELEMENT.currentTime;
     389});
  • main/trunk/model-interfaces-dev/atea/style/asr.scss

    r35294 r35296  
    77    --primary-bg-color-d1: #2785ad;
    88    --primary-bg-color-d2: #227497;
     9
     10    --primary-fg-color: white;
     11
    912    --primary-box-shadow: 0px 2px 4px 0px #505050;
    10 
     13    --primary-box-shadow-thin: 0px 1px 3px 0px #747474;
     14
     15    --paper-color: #FCFCFC;
     16    --page-bg-color: #f7f4f0;
     17
     18    --monospace-font: 16px 'Roboto Mono', sans-serif;
     19
     20    --border-radius: 0.3em;
     21
     22    --transition-duration: 0.15s;
     23}
     24
     25.theme-error {
     26    --primary-bg-color: #f04848;
    1127    --primary-fg-color: white;
    12 
    13     --error-bg-color: #ff4242;
    14     --error-fg-color: black;
    15 
    16     --paper-color: #FCFCFC;
    17 
    18     --monospace-font: 16px 'Roboto Mono', sans-serif;
    19 }
    20 
    21 .theme-error {
    22     --primary-bg-color: #ff4242;
    23     --primary-fg-color: black;
    2428}
    2529
    2630.theme-flat {
    2731    --primary-bg-color: transparent;
    28     --primary-fg-color: #222;
     32    --primary-fg-color: #2191c0;
    2933    --primary-box-shadow: none;
    3034
    3135    &:hover {
    32         --primary-bg-color: #EEE;
    33     }
     36        background-color: #EEE;
     37    }
     38}
     39
     40@mixin transition-set {
     41    -webkit-transition-duration: var(--transition-duration);
     42    -moz-transition-duration: var(--transition-duration);
     43    transition-duration: var(--transition-duration);
    3444}
    3545
     
    4050.paper {
    4151    background-color: var(--paper-color);
    42     padding: 1em;
    43     border-radius: 0.3em;
     52    padding: 1.2em;
     53    border-radius: var(--border-radius);
    4454    box-shadow: var(--primary-box-shadow);
    4555}
    4656
     57.card {
     58    @extend .paper;
     59    margin: 1em;
     60
     61    box-shadow: var(--primary-box-shadow-thin);
     62}
     63
     64.card-outlined {
     65    @extend .card;
     66
     67    box-shadow: none;
     68    border: 1px solid #BBB;
     69}
     70
    4771.divider {
    48     margin: 1em 2em;
    4972    height: 1px;
    5073    background-color: #0000001F;
     
    6689    margin: 0 1px 3px 1px; /* Keeps space around the box shadow */
    6790
    68     -webkit-transition-duration: 0.2s;
    69     transition-duration: 0.2s;
     91    @include transition-set;
    7092
    7193    &:hover {
     
    84106    box-shadow: var(--primary-box-shadow);
    85107
    86     border-radius: 0.3em;
     108    border-radius: var(--border-radius);
    87109    padding: 0.5em;
    88110    cursor: pointer;
     
    92114    margin: 0 1px 3px 1px; /* Keeps space around the box shadow */
    93115
    94     -webkit-transition-duration: 0.15s;
    95     transition-duration: 0.15s;
     116    @include transition-set;
    96117
    97118    span {
     
    109130}
    110131
     132/** Text display/input */
     133
     134.text-container {
     135    display: flex;
     136    align-items: center;
     137
     138    border-radius: var(--border-radius);
     139    //border-bottom: 1px solid #AAA;
     140    background: #DCDCDCDC;
     141    padding: 0.5em;
     142}
     143
     144.text-container-sl {
     145    @extend .text-container;
     146
     147    overflow-x: auto;
     148    overflow-y: hidden;
     149    white-space: nowrap;
     150}
     151
     152.text-input {
     153    display: flex;
     154    align-items: center;
     155
     156    border-radius: var(--border-radius) var(--border-radius) 0 0;
     157    border-bottom: 1px solid #AAA;
     158    background: #EEE;
     159    padding: 0.5em;
     160
     161    @include transition-set;
     162
     163    &:hover {
     164        background-color: #DDD;
     165    }
     166
     167    &:active {
     168        border-bottom: 1px solid var(--primary-bg-color);
     169       
     170        .text-input-active {
     171            width: 100%;
     172        }
     173    }
     174
     175    .text-input-active {
     176        background-color: var(--primary-bg-color);
     177        height: 1px;
     178        margin: 0 auto;
     179        width: 0;
     180
     181        @include transition-set;
     182    }
     183}
     184
     185.text-placeholder {
     186    color: #666;
     187    cursor: default;
     188    font-style: italic;
     189}
     190
    111191/* === End component definitions === */
    112192
     
    143223}
    144224
    145 /* === End theme definitions === */
     225/* Scrollbar replacement */
     226
     227::-webkit-scrollbar {
     228    width: 6px;
     229    height: 6px;
     230}
     231
     232::-webkit-scrollbar-track {
     233    background: #f1f1f1;
     234}
     235
     236::-webkit-scrollbar-thumb {
     237    background: #CCC;
     238    border-radius: 3px;
     239
     240    @include transition-set;
     241
     242    &:hover {
     243        background: #666;
     244        height: 8px;
     245        width: 8px;
     246    }
     247}
    146248
    147249/* === Start theme application === */
     
    151253}
    152254
     255hr {
     256    @extend .divider;
     257}
     258
    153259/* === End theme application === */
    154260
    155261body {
    156     background-color: var(--paper-color);
     262    background-color: var(--page-bg-color);
    157263    font-family: 'Roboto', sans-serif;
    158264    font-size: 16px;
    159265}
    160266
     267#container {
     268    background-color: var(--page-bg-color);
     269}
     270
    161271#gs_content {
    162272    padding: 0;
    163     background-color: var(--paper-color);
     273    background-color: var(--page-bg-color);
    164274}
    165275
     
    171281#audio-transcription-container {
    172282    margin-top: 2em;
     283}
     284
     285.audio-file-picker {
     286    display: grid;
     287    gap: 0.5em 0.5em;
     288    grid-template-columns: auto 1fr auto;
     289    align-items: stretch;
     290
     291    input {
     292        display: none;
     293    }
     294
     295    .text-container {
     296        cursor: pointer;
     297    }
    173298}
    174299
     
    189314
    190315.transcription__container {
    191     padding: 0.5em;
    192 
    193316    -webkit-transition-duration: 0.15s;
    194317    transition-duration: 0.15s;
    195318
    196     &:last-child {
    197         margin-bottom: 0;
    198     }
    199 
    200     &:hover {
    201         background-color: #EEE;
    202     }
     319    // &:hover {
     320    //     background-color: #EEE;
     321    // }
    203322}
    204323
    205324.transcription__header {
     325    display: grid;
     326    gap: 0.5em 0.5em;
     327    grid-template-columns: 1fr auto;
     328    align-items: center;
     329
     330    margin-bottom: 0.5em;
     331}
     332
     333.transcription__word-list {
     334    display: grid;
     335    gap: 0.5em 0.5em;
     336    grid-template-columns: 1fr auto;
     337    align-items: start;
     338
     339    font: var(--monospace-font);
     340    padding-top: 1em;
     341}
     342
     343.transcription__word-list__controls {
    206344    display: flex;
    207     width: 100%;   
    208     justify-content: space-between;
    209     align-items: center;
    210     margin-bottom: 0.5em;
    211 }
    212 
    213 .transcription__file-name {
    214     margin: 0 1em 0 1em;
    215 }
    216 
    217 .transcription__word-list {
    218     font: var(--monospace-font);
     345    flex-direction: column;
     346    align-items: center;
    219347}
    220348
    221349.transcription__word {
    222350    display: inline-block;
    223     margin-top: 1em;
     351    margin-bottom: 1em;
    224352
    225353    &:hover {
     
    230358.transcription__error-container {
    231359    @extend .transcription__container;
    232 
    233     background-color: rgba(255, 0, 0, 0.226);
    234    
    235     &:hover {
    236         background-color: rgba(255, 0, 0, 0.226);
    237     }
     360    display: grid;
     361    gap: 0.5em 0.5em;
     362    grid-template-columns: 1fr auto;
     363    align-items: center;
     364
     365    border-left: 3px solid red;
    238366}
    239367
  • main/trunk/model-interfaces-dev/atea/transform/pages/asr.xsl

    r35294 r35296  
    2020    <!-- set page breadcrumbs -->
    2121    <xsl:template name="breadcrumbs"><gslib:siteLink/><gslib:rightArrow/>
    22     <xsl:if test="$groupPath != ''">
    23       <xsl:for-each select="/page/pageResponse/pathList/group">
    24         <xsl:sort data-type="number" select="@position"/>
    25           <a>
    26         <xsl:attribute name="href"><gslib:groupHref path="{@path}"/></xsl:attribute>               
    27         <xsl:attribute name="title"><gslib:groupName path="{@path}"/></xsl:attribute>
    28         <gslib:groupName path="{@path}"/>
    29           </a>
    30           <gslib:rightArrow/>
    31       </xsl:for-each>
    32     </xsl:if>
     22        <xsl:if test="$groupPath != ''">
     23            <xsl:for-each select="/page/pageResponse/pathList/group">
     24                <xsl:sort data-type="number" select="@position" />
     25                <a>
     26                    <xsl:attribute name="href"><gslib:groupHref path="{@path}"/></xsl:attribute>               
     27                    <xsl:attribute name="title"><gslib:groupName path="{@path}"/></xsl:attribute>
     28                    <gslib:groupName path="{@path}" />
     29                </a>
     30                <gslib:rightArrow/>
     31            </xsl:for-each>
     32        </xsl:if>
    3333    </xsl:template>
    3434
     
    3737        <xsl:call-template name="audio-transcription"/>
    3838    </xsl:template>
    39 
    40     <!-- <xsl:attribute-set name="vue">
    41         <xsl:attribute name="v-on" />
    42         <xsl:attribute name="v-bind" />
    43     </xsl:attribute-set> -->
    4439   
    4540    <!-- Template for processing audio file uploads -->
     
    4843        <link href="interfaces/{$interface_name}/style/asr.css" rel="stylesheet" type="text/css" />
    4944
    50         <section id="audio-transcription-container" class="paper">
     45        <section id="audio-transcription-container">
    5146            <!-- Used to calculate the character size of our monospace font -->
    5247            <span class="monospace-font-size">ngā tama a rangi</span>
    5348
    5449            <!-- Contains the file input, transcribe button and transcription progress indicator -->
    55             <div id="audioUploadContainer">
     50            <div id="audioUploadContainer" class="audio-file-picker">
    5651                <button type="button" v-on:click="openFilePicker"
    5752                        v-bind:disabled="isTranscribing">
     
    6055                </button>
    6156
     57                <div class="text-container-sl" v-on:click="openFilePicker">
     58                    <span class="text-placeholder" v-if="!anyFiles">Select file/s...</span>
     59                    <span v-if="anyFiles">{{ getFileNameList }}</span>
     60                </div>
     61
    6262                <input id="audioFileInput" type="file" v-on:input="onFilesChanged"
    6363                       accept="audio/wav" multiple="multiple" v-bind:disabled="isTranscribing" />
    6464
    6565                <button style="float: right;" type="submit"
    66                         v-bind:disabled="!canTranscribe" v-on:click="doTranscription">
     66                        v-bind:disabled="!anyFiles" v-on:click="doTranscription">
    6767                    <span class="material-icons">&#xEA3E;</span> <!-- history_edu -->
    6868                    <span>Transcribe</span>
     
    7575            <div id="transcriptionsDisplayContainer">
    7676
    77                 <audio id="transcriptionAudio" v-on:timeupdate="">
     77                <audio id="transcriptionAudio">
    7878                    <source id="transcriptionAudioSource" />
    7979                </audio>
     
    8181                <ul class="transcription__list">
    8282                    <!-- Displays any failed transcriptions -->
    83                     <li v-for="failure in failures" class="transcription__error-container">
    84                         Failed to transcribe <i v-if="failure.file">{{ failure.file }}</i><br/>
    85                         <span v-if="failure.message">Reason: {{ failure.message }}</span>
     83                    <li v-for="[key, value] in failures">
     84                        <div class="transcription__error-container card">
     85                            <div>
     86                                Failed to transcribe <i v-if="value.file">{{ value.file }}</i><br/>
     87                                <span v-if="value.message">Reason: {{ value.message }}</span>
     88                            </div>
     89                           
     90                            <button class="btn-fab theme-flat" v-on:click="removeFailure(key)">
     91                                <span class="material-icons">&#xE14C;</span> <!-- clear -->
     92                            </button>
     93                        </div>
    8694                    </li>
    8795
    88                     <li v-for="transcription in getTranscriptions" class="transcription__container">
    89                         <div class="transcription__header">
    90                             <button class="btn-fab theme-flat" v-on:click="playAudioFile(transcription.fileName)" type="button">
    91                                 <span class="material-icons">&#xE037;</span> <!-- play_arrow -->
    92                             </button>
     96                    <!-- Displays each transcription -->
     97                    <li v-for="transcription in getTranscriptions">
     98                        <div class="transcription__container card">
     99                            <!-- Header containing info and actions for the transcription -->
     100                            <div class="transcription__header">
     101                                <span>File: {{ transcription.fileName }}</span>
    93102
    94                             <p class="transcription__text">{{ transcription.transcription }}</p>
    95                             <p class="body2 transcription__file-name">File: {{ transcription.fileName }}</p>
     103                                <button class="theme-error" v-on:click="removeTranscription(transcription.id)" type="button">
     104                                    <span class="material-icons">delete</span>
     105                                    <span>Remove</span>
     106                                </button>
     107                            </div>
    96108
    97                             <button class="btn-fab theme-error" v-on:click="removeTranscription(transcription.id)" type="button">
    98                                 <span class="material-icons">&#xE872;</span> <!-- delete -->
    99                             </button>
    100                         </div>
     109                            <div class="text-container">
     110                                {{ transcription.transcription }}
     111                            </div>
    101112
    102                         <div class="transcription__word-list">
    103                             <input type="checkbox" v-model="showCharDisplay" />
    104                             <ul class="transcription__list">
    105                                 <li v-if="!showCharDisplay">
    106                                     <span v-for="word in getWords(transcription.id)"
    107                                           class="transcription__word"
    108                                           v-on:click="playAudioFile(transcription.fileName, word.startTime)">
     113                            <div class="transcription__word-list collapse" v-if="showWordList">
     114                                <div class="transcription__word-list__controls">
     115                                    <button class="btn-fab" v-on:click="playAudioFile(transcription.fileName)" type="button">
     116                                        <span class="material-icons">play_arrow</span>
     117                                    </button>
     118
     119                                    <input type="checkbox" v-model="showCharDisplay" />
     120                                </div>
     121                               
     122                                <ul class="transcription__list">
     123                                    <li v-if="!showCharDisplay">
     124                                        <span v-for="word in getWords(transcription.id)"
     125                                        class="transcription__word"
     126                                        v-on:click="playAudioFile(transcription.fileName, word.startTime)">
    109127                                        {{ word.word }}
    110128                                    </span>
     
    112130                                <li v-if="showCharDisplay">
    113131                                    <span v-for="char in getChars(transcription.id)"
    114                                           class="transcription__word"
    115                                           v-on:click="playAudioFile(transcription.fileName, char.startTime)">
    116                                         {{ char.char }}
    117                                     </span>
    118                                 </li>
    119                                 <!-- <li v-for="line in getLines(transcription.id)" style="display: flex">
    120                                     <span v-for="word in line.words" style="border: 1px solid blue">{{ word }}</span>
    121                                     <input type="range" min="0" max="100" />
    122                                 </li> -->
    123                             </ul>
     132                                    class="transcription__word"
     133                                    v-on:click="playAudioFile(transcription.fileName, char.startTime)">
     134                                    {{ char.char }}
     135                                        </span>
     136                                    </li>
     137                                </ul>
     138                            </div>
     139
     140                            <hr />
     141
     142                            <button type="button" class="theme-flat" v-on:click="toggleWordList">
     143                                <span class="material-icons" v-if="!showWordList">expand_more</span>
     144                                <span class="material-icons" v-if="showWordList">expand_less</span>
     145                                <span>Expand</span>
     146                            </button>
    124147                        </div>
    125 
    126                         <hr class="divider" />
    127148                    </li>
    128149                </ul>
Note: See TracChangeset for help on using the changeset viewer.