From fc28f5cd5a4381b2282c85fe2491be4dbcbfda93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Volds=C3=A6ter?= <ole.voldsater@uib.no> Date: Thu, 21 Jan 2021 12:37:32 +0100 Subject: [PATCH] visning av flere artikler samtidig --- src/App.vue | 122 ++++++++++++++-------- src/assets/down_arrow.png | Bin 0 -> 319 bytes src/components/Article.vue | 17 ++- src/components/CompoundList.vue | 7 +- src/components/DefElement.vue | 7 +- src/components/Definition.vue | 13 ++- src/components/Example.vue | 7 +- src/components/Header.vue | 4 +- src/components/SearchResults.vue | 35 ++++--- src/components/SubArticle.vue | 9 +- src/components/inflection/Adjective.vue | 5 +- src/components/inflection/AdjectiveMF.vue | 5 +- src/components/inflection/Noun.vue | 5 +- src/components/inflection/Verb.vue | 5 +- src/main.js | 4 + 15 files changed, 155 insertions(+), 90 deletions(-) create mode 100644 src/assets/down_arrow.png diff --git a/src/App.vue b/src/App.vue index 6db69bde..dcff51aa 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,23 +5,26 @@ <span class="top">EID AV UNIVERSITETET I BERGEN OG SPRÅKRÅDET</span> <p class="mission-statement">Bokmålsordboka og Nynorskordboka er dei einaste fritt tilgjengelege ordbøkene som gjer svar på korleis ord skal skrivast og bøyast i norsk i tråd med gjeldande rettskriving.</p> - <autocomplete :debounceTime="100" :auto-select="true" :search="search" @submit="select_result" placeholder="søk..." ref="search"> - <template #result="{result, props}"> - <Preview v-bind="props" :searchHit="result"> - </Preview> - </template> - </autocomplete> - <span class="lang-select-intro">SØK PÅ</span> - <input type="radio" id="radio_both" value="bob,nob" v-model="lang"> - <label for="radio_both">Begge</label> - <input type="radio" id="radio_bob" value="bob" v-model="lang"> - <label for="radio_bob">Bokmål</label> - <input type="radio" id="radio_nob" value="nob" v-model="lang"> - <label for="radio_nob">Nynorsk</label> + <div class="search_container"> + <autocomplete :debounceTime="100" :auto-select="true" :search="search" @submit="select_result" placeholder="søk..." ref="search"> + <template #result="{result, props}"> + <li class="suggestion" v-bind="props">{{result.label}}</li> + </template> + </autocomplete> + <div class="lang_select_container"> + <select class="lang_select" name="lang" v-model="lang"> + <option value="bob,nob">Begge målformer </option> + <option value="bob">Bokmål</option> + <option value="nob">Nynorsk</option> + </select> + </div> + </div> </header> <img id="spinner" :class="waiting ? 'show' : 'hide'" src="./assets/spinner.gif" alt="Venter på innhold" /> - <SearchResults :hits="search_results" :lang="lang" @search-hit-click="search_hit_click" v-show="! waiting" /> - <Article :key="article_key" :article="article" @article-click="article_link_click" /> + <SearchResults :hits="search_results" :lang="lang" @article-click="article_link_click" v-show="! waiting" /> + <div id="single_article_container"> + <Article :key="article_key" :article="article" @article-click="article_link_click" /> + </div> <div class="welcome" v-show="! (article.error || article.lemmas.length || search_results.length || waiting)"> <p>Bør ordet <em>crew</em> inn i dei norske standardordbøkene? Korleis definerar vi <em>c-kjendis</em>, og korleis bøyar vi eigentleg <em>abaya</em> på nynorsk - er det fleire <em>abayaer</em> eller <em>abayaar</em>? Blir <em>en vegetar</em> @@ -75,14 +78,11 @@ window.onpopstate = function (event) { } } -function navigate_to_article(self, article_id, origin_article, origin_lemma) { - if (origin_article) { - self.$plausible.trackEvent('internal link incoming', {props: {origin: `/${self.$route.params.lang}/${origin_article}/${origin_lemma || '_'}`}}) - } - - axios.get(self.api_pref + '' + article_id) +function navigate_to_article(self) { + axios.get(api_endpoint + '/' + self.$route.params.lang + '/article/' + self.$route.params.id) .then(function(response){ self.article = response.data + self.search_results = [] }) .catch(function(error){ if (error.response && error.response.status == 404) { @@ -100,7 +100,7 @@ function navigate_to_article(self, article_id, origin_article, origin_lemma) { }) .then(function(response){ self.waiting_for_articles = false - history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang}, '') + history.replaceState({article: self.article, search_results: [], lang: self.lang}, '') }) } @@ -113,6 +113,15 @@ function navigate_to_search(self, query) { }) } +function navigate_to_word(self, word) { + axios.get(self.api_pref + 'suggest?q=' + word) + .then(function(response){ + self.search_results = response.data.filter(result => result.match.length == word.length) + self.waiting_for_articles = false + history.replaceState({article: self.article, search_results: self.search_results, lang: self.lang}, '') + }) +} + export default { name: 'app', data: function() { @@ -139,9 +148,15 @@ export default { return new Promise(resolve => { return axios.get(self.api_pref + 'suggest?q=' + q).then( function(response) { - let hits = q.length ? [{}] : [] - hits = hits.concat(response.data) - resolve(hits.map(h => Object.assign(h, {q: q}))) + let hits = q.length ? [{q: q, label: q + ' (alt)'}] : [] + response.data.forEach((item, i) => { + if (hits[0].label != item.match) { + hits.splice(0, 0, {q: q, label: item.match, articles: []}) + } + hits[0].articles.push(item) + }); + hits.reverse() + resolve(hits) }) }) } @@ -157,10 +172,10 @@ export default { select_result: function(event) { this.$refs.search.value = '' document.activeElement.blur() - if(event.body){ - this.$router.push('/' + event.dictionary + '/' + event.article_id + '/' + event.match) - this.search_results = [] - this.article = event + if(event.articles){ + this.$router.push('/' + this.lang + '/w/' + event.label) + this.search_results = event.articles + this.article = {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}} history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '') this.$plausible.trackEvent('dropdown selection', {props: {query: event.q, dictionary: event.dictionary, match: event.match}}) }else{ @@ -175,11 +190,9 @@ export default { this.article_key++ history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '') }else{ - let current_art_id = this.article.article_id - let current_lemma = this.article.lemmas[0].lemma this.article = {lemmas: [], body:{pronunciation: [], definitions: [], etymology: []}} this.waiting_for_articles = true - navigate_to_article(this, item.article_id, current_art_id, current_lemma) + navigate_to_article(this) } }, search_hit_click: function(article){ @@ -202,22 +215,18 @@ export default { self.waiting_for_nob_metadata = false }) - this.lang = this.$route.params.lang || this.$route.query.lang || 'bob,nob' - if(this.$route.query.q) { - navigate_to_search(this, this.$route.query.q) + this.lang = 'bob,nob' + + if(this.$route.name == 'word') { + navigate_to_word(this, this.$route.params.word) } - else if(this.$route.params.id){ + else if(this.$route.name == 'lookup'){ navigate_to_article(this, this.$route.params.id) } else { this.waiting_for_articles = false history.replaceState({article: this.article, search_results: this.search_results, lang: this.lang}, '') } - }, - watch: { - $route() { - this.lang = this.$route.params.lang || this.$route.query.lang || 'bob,nob' - } } } </script> @@ -249,7 +258,7 @@ main { background-attachment: fixed; } -header, #search_results, #spinner, article, footer, div.welcome { +header, #search_results, #spinner, #single_article_container, footer, div.welcome { padding-left: calc((100vw - 1000px) / 2); padding-right: calc((100vw - 1000px) / 2); background-image: url('./assets/beta.png'); @@ -333,6 +342,15 @@ footer { padding-bottom: 20px; } +.search_container { + display: flex; + flex-direction: row; +} + +li.suggestion { + font-weight: bold; +} + footer p { padding-left: 20px; padding-right: 20px; @@ -350,4 +368,24 @@ footer p { padding: 10px; padding-left: 20px; } + +select.lang_select { + appearance: none; + background-color: transparent; + border: none; + padding: 0 1em 0 0; + margin: 0; + width: 100%; + height: 100%; + font-family: inherit; + font-size: inherit; + font-weight: bold; + cursor: inherit; + line-height: inherit; + background-image: url('./assets/down_arrow.png'); + background-position: 98% center; + background-repeat: no-repeat; + background-size: 10%; +} + </style> diff --git a/src/assets/down_arrow.png b/src/assets/down_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..d07a042e5c09a86a29b3bee864adde459964aa6f GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?K3?%mjbVdLv&H$efS0D`m&B-qh09i~WL4Lsu z4YRfVB!Ilfo-U3d5r^Mi-O0;r$m4P`^^sD<*=vupB95q)Of_S1yx@3l2ZP_A=62g9 zuUB2o+A4MP_vCZY3o1Wsc`%jz8q>+<*$R8KMTHp-RVlPgP^z)fI@%(Vs4U>*Az-&x z|7Z(KVz5GsfKtuR3=v_c7Trbxr-l!2Plx(&2yA0@;$T12f8X8kp~DS94#gY}{(p%_ zTX+*46<RhZ*w}c82#d9}HVPbS`S8Rf*k^{oG!`cg=7%pQu9{&WFpbTLL-yf=m~fvL z9LzvNoh|mnhzLs^N>FI|;B2w;;TOe&r#%$DhpxVw_5UsR@kJc_14<7r0R@AntDnm{ Hr-UW|Nr!aE literal 0 HcmV?d00001 diff --git a/src/components/Article.vue b/src/components/Article.vue index 5a458211..9dbf0af9 100644 --- a/src/components/Article.vue +++ b/src/components/Article.vue @@ -1,22 +1,22 @@ <template> <article v-show="article.lemmas.length || article.error"> - <Header :lemmas="article.lemmas" /> + <Header :lemmas="article.lemmas" :dictionary="dictionary" /> <section v-if="! article.error && article.body.pronunciation && article.body.pronunciation.length"> <h3>Uttale</h3> <ul> - <DefElement v-for="(element, index) in article.body.pronunciation" :key="index" :body='element' @article-click="article_link_click" /> + <DefElement v-for="(element, index) in article.body.pronunciation" :dictionary="dictionary" :key="index" :body='element' @article-click="article_link_click" /> </ul> </section> <section v-if="! article.error && article.body.etymology && article.body.etymology.length"> <h3>Etymologi</h3> <ul> - <DefElement v-for="(element, index) in article.body.etymology" :key="index" :body='element' @article-click="article_link_click" /> + <DefElement v-for="(element, index) in article.body.etymology" :dictionary="dictionary" :key="index" :body='element' @article-click="article_link_click" /> </ul> </section> <section v-if="! article.error"> <h3>Definisjoner</h3> <ol> - <Definition v-for="definition in article.body.definitions" :level="1" :key="definition.id" :body='definition' @article-click="article_link_click" /> + <Definition v-for="definition in article.body.definitions" :dictionary="dictionary" :level="1" :key="definition.id" :body='definition' @article-click="article_link_click" /> </ol> </section> <section v-if="article.error"> @@ -36,6 +36,11 @@ export default { props: { article: Object }, + computed: { + dictionary: function() { + return this.article.dictionary || this.$route.params.lang + } + }, components: { DefElement, Definition, @@ -72,6 +77,10 @@ li.level1.definition { list-style: upper-alpha; } +li.level2.definition { + list-style: decimal; +} + li.level3.definition { list-style: lower-alpha; } diff --git a/src/components/CompoundList.vue b/src/components/CompoundList.vue index d5cd17b1..fd29e976 100644 --- a/src/components/CompoundList.vue +++ b/src/components/CompoundList.vue @@ -1,9 +1,9 @@ <template> <li class="compound_list"> <ul> - <DefElement :body="body.intro" v-if="body.intro" /> + <DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" /> <li :key="index" v-for="(item, index) in body.elements"> - <router-link :to="item.article_id + (item.definition_id ? '#def'+item.definition_id : '')" @click.native="article_link_click(item)"> + <router-link :to="'/' + dictionary + '/' + item.article_id + (item.definition_id ? '#def'+item.definition_id : '')" @click.native="article_link_click(item)"> <span class="homograph" :key="index">{{item.lemmas[0].hgno ? String.fromCharCode(0x215f + item.lemmas[0].hgno) : ''}}</span> {{item.lemmas[0].lemma}}; </router-link> @@ -18,7 +18,8 @@ import DefElement from './DefElement.vue' export default { name: 'CompoundList', props: { - body: Object + body: Object, + dictionary: String }, components: { DefElement diff --git a/src/components/DefElement.vue b/src/components/DefElement.vue index 416257a1..d1e40f40 100644 --- a/src/components/DefElement.vue +++ b/src/components/DefElement.vue @@ -21,11 +21,12 @@ export default { tag: { type: String, default: 'li' - } + }, + dictionary: String }, computed: { unparsed: function(){ - let lang = this.$route.params.lang + let lang = this.dictionary let path_lemma = this.$route.params.lemma return this.body.items.map( function(item){ @@ -34,7 +35,7 @@ export default { type: item.type_, html: '', link_text: item.word_form || item.lemmas[0].lemma, - ref: (path_lemma ? '../' : '') + item.article_id + '/' + (item.word_form || item.lemmas[0].lemma) + (item.definition_id ? '#def' + item.definition_id : ''), + ref: '/' + lang + '/' + item.article_id + '/' + (item.word_form || item.lemmas[0].lemma) + (item.definition_id ? '#def' + item.definition_id : ''), article_id: item.article_id, definition_id: item.definition_id, definition_order: item.definition_order diff --git a/src/components/Definition.vue b/src/components/Definition.vue index d1b2c2d3..1d8d1574 100644 --- a/src/components/Definition.vue +++ b/src/components/Definition.vue @@ -1,13 +1,13 @@ <template> <li :class="['definition', 'level'+level]" :ref="'def' + body.id" :id="'def' + body.id"> <ul class="explanation"> - <li :is="element_wrapper.template" :body="element_wrapper.element" v-for="(element_wrapper, index) in template_name_added" :key="index" @article-click="article_link_click">{{element_wrapper.element}}</li> + <li :is="element_wrapper.template" :body="element_wrapper.element" :dictionary="element_wrapper.dictionary" v-for="(element_wrapper, index) in template_name_added" :key="index" @article-click="article_link_click">{{element_wrapper.element}}</li> </ul> <div :is="level < 3 ? 'ol' : 'ul'" class="sub_definitions" v-if="subdefs.length"> - <Definition :level="level+1" :body="subdef" v-for="(subdef, index) in subdefs" :key="index" @article-click="article_link_click" /> + <Definition :level="level+1" :body="subdef" v-for="(subdef, index) in subdefs" :dictionary="dictionary" :key="index" @article-click="article_link_click" /> </div> <ul class="sub_articles" v-if="subArticles.length"> - <SubArticle :body="subart" v-for="(subart, index) in subArticles" :key="index" @article-click="article_link_click" /> + <SubArticle :body="subart" v-for="(subart, index) in subArticles" :dictionary="dictionary" :key="index" @article-click="article_link_click" /> </ul> </li> </template> @@ -22,7 +22,8 @@ var Definition = { name: 'Definition', props: { body: Object, - level: Number + level: Number, + dictionary: String }, components: { DefElement, @@ -33,6 +34,7 @@ var Definition = { }, computed: { template_name_added: function(){ + let dictionary = this.dictionary return this.body.elements.filter(el => ! ['definition', 'sub_article'].includes(el.type_)).map( function(element){ return { @@ -41,7 +43,8 @@ var Definition = { 'example': 'Example', 'compound_list': 'CompoundList' }[element.type_] || 'li', - 'element': element + 'element': element, + 'dictionary': dictionary } }) }, diff --git a/src/components/Example.vue b/src/components/Example.vue index 9ec0e192..809ce182 100644 --- a/src/components/Example.vue +++ b/src/components/Example.vue @@ -1,7 +1,7 @@ <template> <li class="example"> - <DefElement tag="q" :body="body.quote" @article-click="article_link_click" /><span v-if="body.explanation && body.explanation.content.length"> - </span> - <DefElement tag="span" :body="body.explanation" v-if="body.explanation && body.explanation.content.length" @article-click="article_link_click" /> + <DefElement tag="q" :body="body.quote" @article-click="article_link_click" :dictionary="dictionary" /><span v-if="body.explanation && body.explanation.content.length"> - </span> + <DefElement tag="span" :body="body.explanation" v-if="body.explanation && body.explanation.content.length" @article-click="article_link_click" :dictionary="dictionary" /> </li> </template> @@ -11,7 +11,8 @@ import DefElement from './DefElement.vue' export default { name: 'Example', props: { - body: Object + body: Object, + dictionary: String }, components: { DefElement diff --git a/src/components/Header.vue b/src/components/Header.vue index 8b7c326b..6dc316f3 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -11,6 +11,7 @@ <component v-for="grp in Object.keys(lemma.inflection_groups)" :key="grp.replace('/', '_')" :is="grp.replace('/', '_')" + :dictionary="dictionary" :standardisations="lemma.inflection_groups[grp]"></component> </div> </div> @@ -36,7 +37,8 @@ import VERB_sPass from './inflection/VerbSPass.vue' export default { name: 'Header', props: { - lemmas: Array + lemmas: Array, + dictionary: String }, computed: { group_list: function() { diff --git a/src/components/SearchResults.vue b/src/components/SearchResults.vue index 31aa2ce4..d1c3cc73 100644 --- a/src/components/SearchResults.vue +++ b/src/components/SearchResults.vue @@ -4,25 +4,24 @@ <div class="flex-container"> <ul class="hits" v-if="results_bob.length"> <li><h4>Bokmål</h4></li> - <Preview v-for="(result, index) in results_bob" :key="index" :searchHit="result" @click.native="article_link_click(result)"> - </Preview> + <li class="article_container" v-for="(result, index) in results_bob" :key="index"> + <Article :article="result" @article-click="article_link_click"> + </Article> + </li> </ul> <ul class="hits" v-if="results_nob.length"> <li><h4>Nynorsk</h4></li> - <Preview v-for="(result, index) in results_nob" :key="index" :searchHit="result" @click.native="article_link_click(result)"> - </Preview> - </ul> - <ul class="hits" v-if="results_norsk.length"> - <li><h4>Norsk Ordbok</h4></li> - <Preview v-for="(result, index) in results_norsk" :key="index" :searchHit="result" @click.native="article_link_click(result)"> - </Preview> + <li class="article_container" v-for="(result, index) in results_nob" :key="index"> + <Article :article="result" @article-click="article_link_click"> + </Article> + </li> </ul> </div> </section> </template> <script> -import Preview from './Preview.vue' +import Article from './Article.vue' export default { name: 'SearchResults', @@ -36,19 +35,15 @@ export default { }, results_nob: function(){ return this.hits.filter(hit => hit.dictionary == 'nob') - }, - results_norsk: function(){ - return this.hits.filter(hit => hit.dictionary == 'norsk_ordbok') } }, methods: { - article_link_click: function(result) { - this.$router.push('/' + result.dictionary + '/' + result.article_id) - this.$emit('search-hit-click', result) + article_link_click: function(item) { + this.$emit('article-click', item) } }, components: { - Preview + Article } } @@ -71,6 +66,8 @@ export default { .hits { margin-top:0px; + margin-left: 20px; + margin-right: 20px; } .flex-container { @@ -84,4 +81,8 @@ export default { .flex-container h4 { margin: 0px; } + + li.article_container { + border-bottom: solid 2px #560027; + } </style> diff --git a/src/components/SubArticle.vue b/src/components/SubArticle.vue index af1bb697..8d499eee 100644 --- a/src/components/SubArticle.vue +++ b/src/components/SubArticle.vue @@ -1,13 +1,13 @@ <template> <li class="sub_article"> <span class="sub_article_header"> - <router-link :to="link_prefix + body.article_id" @click.native="article_link_click(body)"> + <router-link :to="'/' + dictionary + '/' + body.article_id" @click.native="article_link_click(body)"> {{body.lemmas[0]}} </router-link> </span> <ul> - <DefElement :body="body.intro" v-if="body.intro" @article-click="article_link_click" /> - <Definition :level="9" :body="body.article.body.definitions[0]" @article-click="article_link_click" /> + <DefElement :body="body.intro" v-if="body.intro" :dictionary="dictionary" @article-click="article_link_click" /> + <Definition :level="9" :body="body.article.body.definitions[0]" :dictionary="dictionary" @article-click="article_link_click" /> </ul> </li> </template> @@ -18,7 +18,8 @@ import DefElement from './DefElement.vue' export default { name: 'SubArticle', props: { - body: Object + body: Object, + dictionary: String }, components: { DefElement diff --git a/src/components/inflection/Adjective.vue b/src/components/inflection/Adjective.vue index cbe6d50b..88ace29c 100644 --- a/src/components/inflection/Adjective.vue +++ b/src/components/inflection/Adjective.vue @@ -32,14 +32,15 @@ export default { name: "ADJ", props: { - standardisations: Array + standardisations: Array, + dictionary: String }, computed: { i18n: function() { return { bob: ['Entall', 'Flertall', 'Hun', 'Intet', 'estemt'], nob: ['Eintal', 'Fleirtal', 'Ho', 'Inkje', 'unden'] - }[this.$route.params.lang] + }[this.dictionary] } } } diff --git a/src/components/inflection/AdjectiveMF.vue b/src/components/inflection/AdjectiveMF.vue index f0d7712d..daea6517 100644 --- a/src/components/inflection/AdjectiveMF.vue +++ b/src/components/inflection/AdjectiveMF.vue @@ -34,14 +34,15 @@ export default { name: "ADJ_masc_fem", props: { - standardisations: Array + standardisations: Array, + dictionary: String }, computed: { i18n: function() { return { bob: ['Entall', 'Flertall', 'Hun', 'Intet', 'estemt'], nob: ['Eintal', 'Fleirtal', 'Ho', 'Inkje', 'unden'] - }[this.$route.params.lang] + }[this.dictionary] } } } diff --git a/src/components/inflection/Noun.vue b/src/components/inflection/Noun.vue index 32fa3bfa..78328bd0 100644 --- a/src/components/inflection/Noun.vue +++ b/src/components/inflection/Noun.vue @@ -20,14 +20,15 @@ export default { name: "NOUN", props: { - standardisations: Array + standardisations: Array, + dictionary: String }, computed: { i18n: function() { return { bob: ['Entall', 'Flertall', 'Ubestemt', 'Bestemt', {Masc: ['mask.', 'en'], Fem: ['fem.', 'ei'], Neuter: ['nøytr.', 'et']}], nob: ['Eintal', 'Fleirtal', 'Ubunden', 'Bunden', {Masc: ['mask.', 'ein'], Fem: ['fem.', 'ei'], Neuter: ['nøytr.', 'eit']}] - }[this.$route.params.lang] + }[this.dictionary] } } } diff --git a/src/components/inflection/Verb.vue b/src/components/inflection/Verb.vue index 4f5b0c3c..c14cb634 100644 --- a/src/components/inflection/Verb.vue +++ b/src/components/inflection/Verb.vue @@ -36,14 +36,15 @@ export default { name: "VERB", props: { - standardisations: Array + standardisations: Array, + dictionary: String }, computed: { i18n: function() { return { bob: ['hun', 'Intet', 'Bestemt', 'Flertall'], nob: ['ho', 'Inkje', 'Bunden', 'Fleirtal'] - }[this.$route.params.lang] + }[this.dictionary] } } } diff --git a/src/main.js b/src/main.js index 03ca4fcc..64591997 100644 --- a/src/main.js +++ b/src/main.js @@ -19,6 +19,10 @@ const router = new VueRouter({ name: 'root', path: '/', component: App }, // No props, no nothing + { + name: 'word', + path: '/:lang/w/:word' + }, { name: 'lookup', path: '/:lang/:id(\\d+)/:lemma?', -- GitLab