From dfc66876bfa9f155235a7a694d154f3cfe02079b Mon Sep 17 00:00:00 2001 From: dcz <dcz@ipipan.waw.pl> Date: Mon, 23 Oct 2023 09:46:24 +0200 Subject: [PATCH] First commit. --- entries/templates/entries_base.html | 1 + entries/templates/lu_free.html | 24 + entries/urls.py | 1 + entries/views.py | 21 + .../components/unification/free_lu/FreeLu.vue | 73 +++ .../unification/free_lu/FreeLuEdit.vue | 556 ++++++++++++++++++ .../unification/free_lu/FreeLuElement.vue | 188 ++++++ .../unification/free_lu/FreeLuPreview.vue | 64 ++ .../unification/free_lu/FreeLuRightPane.vue | 68 +++ frontend/src/main.js | 2 + 10 files changed, 998 insertions(+) create mode 100644 entries/templates/lu_free.html create mode 100644 frontend/src/components/unification/free_lu/FreeLu.vue create mode 100644 frontend/src/components/unification/free_lu/FreeLuEdit.vue create mode 100644 frontend/src/components/unification/free_lu/FreeLuElement.vue create mode 100644 frontend/src/components/unification/free_lu/FreeLuPreview.vue create mode 100644 frontend/src/components/unification/free_lu/FreeLuRightPane.vue diff --git a/entries/templates/entries_base.html b/entries/templates/entries_base.html index 2e27f1d..85d9c3b 100644 --- a/entries/templates/entries_base.html +++ b/entries/templates/entries_base.html @@ -33,6 +33,7 @@ <li class="nav-item mr-1"><a onclick='window.location.replace(window.currUnifiedFrameId ? "/pl/entries/hierarchy/?unified_frame_id="+window.currUnifiedFrameId : "/pl/entries/hierarchy")' class="nav-link cursor-pointer">{% trans "Hierarchia" %}</a></li> + <li class="nav-item mr-1"><a href="{% url 'entries:lu_free' %}" class="nav-link">{% trans "Wolne jednostki" %}</a></li> {% endif %} <li class="nav-item dropdown mr-1"> diff --git a/entries/templates/lu_free.html b/entries/templates/lu_free.html new file mode 100644 index 0000000..61a407d --- /dev/null +++ b/entries/templates/lu_free.html @@ -0,0 +1,24 @@ +{% extends "entries_base.html" %} + +{% load i18n %} +{% load static %} + +{% block title %}{% trans "HasÅ‚a" %}{% endblock %} + +{% block scripts %} + {{ block.super }} + <link rel="stylesheet" type="text/css" href="{% static 'entries/css/unification_frames.css' %}"> + <link rel="stylesheet" type="text/css" href="{% static 'common/css/role_colours.css' %}"> + <script src="{% static 'entries/js/unification_entries_list.js' %}"></script> + <script src="{% static 'entries/js/unification_frames_list.js' %}"></script> + <script src="{% static 'entries/js/unification_entries_for_frames_list.js' %}"></script> + <script src="{% static 'entries/js/jquery-impromptu.min.js' %}"></script> + <script> + window.currUnifiedFrameId = {{ unified_frame_id|default:'null' }}; + </script> +{% endblock %} + +{% block modals %} + {{ block.super }} + <div id="lexical-unit-notes-template" class="d-none">{% include 'notes.html' %}</div> +{% endblock %} diff --git a/entries/urls.py b/entries/urls.py index 445b935..3ff50d6 100644 --- a/entries/urls.py +++ b/entries/urls.py @@ -19,6 +19,7 @@ urlpatterns = [ path('change_show_linked_entries/', views.change_show_linked_entries, name='change_show_linked_entries'), path('unification/', views.unification, name='unification'), path('hierarchy/', views.hierarchy, name='hierarchy'), + path('lu_free/', views.lu_free, name='lu_free'), path('autocomplete/', autocompletes.autocomplete, name='autocomplete'), path('plWN_context_lookup/', ajax_plWN_context_lookup, name='plWN_context_lookup'), diff --git a/entries/views.py b/entries/views.py index ce69c16..ee21da5 100644 --- a/entries/views.py +++ b/entries/views.py @@ -113,6 +113,27 @@ def hierarchy(request): ) +@login_required +def lu_free(request): + + unified_frame_id = None + if "unified_frame_id" in request.GET: + unified_frame_id = request.GET.get("unified_frame_id") + + return render( + request, + 'lu_free.html', + { + 'is_vue_app': True, + 'unified_frame_id': unified_frame_id, + 'entries_form': EntryForm(), + 'frames_form': FrameFormFactory.get_form(as_subform=False), + 'schemata_form': SchemaFormFactory.get_form(as_subform=False), + 'unified_frames_form': UnifiedFrameFormFactory.get_form(as_subform=False), + }, + ) + + FORM_TYPES = { 'entry' : EntryForm, } diff --git a/frontend/src/components/unification/free_lu/FreeLu.vue b/frontend/src/components/unification/free_lu/FreeLu.vue new file mode 100644 index 0000000..280e9d2 --- /dev/null +++ b/frontend/src/components/unification/free_lu/FreeLu.vue @@ -0,0 +1,73 @@ +<script> +import UnificationFramesList from "../Unification/UnificationFramesList.vue"; +import FreeLuRightPane from "./FreeLuRightPane.vue"; + +export default { + data () { + return { + entryId: null, + lexicalUnitId: null, + unifiedFrameId: null, + gettext: window.gettext, + unificationEntriesListRefreshKey: 1, + }; + }, + components: {FreeLuRightPane, UnificationFramesList}, + methods: { + lexicalUnitSelected (entryId, lexicalUnitId) { + this.entryId = entryId; + this.lexicalUnitId = lexicalUnitId; + }, + unifiedFrameSelected (unifiedFrameId, entryId, lexicalUnitId) { + this.unifiedFrameId = unifiedFrameId; + this.entryId = entryId; + this.lexicalUnitId = lexicalUnitId; + }, + refreshEntriesList() { + this.unificationEntriesListRefreshKey++; + } + }, + setup() { + const unified_frame_id = new URL(location.href).searchParams.get('unified_frame_id'); + return { + initial_unified_frame_id: parseInt(unified_frame_id), + }; + }, + mounted () { + this.unifiedFrameSelected(window.currUnifiedFrameId); + $('#entries-list').length && Split(['#entries-list', '#entry-display'], { + sizes: [20, 80], + gutterSize: 4, + minSize: 10, + elementStyle: (dimension, size, gutterSize) => { + return { + 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' + } + }, + }); + }, +}; +</script> + +<template> + <div id="entries-list" class="col h-100 w-100 pr-0 overflow-hidden"> + <div id="entries-list-div" class="col p-0 h-100 w-100 overflow-hidden"> + <unification-frames-list + :unificationEntriesListRefreshKey="unificationEntriesListRefreshKey" + :initialUnifiedFrameId="initial_unified_frame_id" + :setupHierarchyMarking="true" + @unified-frame-selected="unifiedFrameSelected" + /> + </div> + </div> + <div id="entry-display" class="col h-100 p-0 overflow-hidden"> + <free-lu-right-pane + ref="hierarchyRightPane" + :entryId="entryId" + :lexicalUnitId="lexicalUnitId" + :initialUnifiedFrameId="unifiedFrameId" + @refresh-entries-list="refreshEntriesList" + /> + </div> +</template> + diff --git a/frontend/src/components/unification/free_lu/FreeLuEdit.vue b/frontend/src/components/unification/free_lu/FreeLuEdit.vue new file mode 100644 index 0000000..58881e1 --- /dev/null +++ b/frontend/src/components/unification/free_lu/FreeLuEdit.vue @@ -0,0 +1,556 @@ +<script> +import InfoTooltip from "../../shared/InfoTooltip.vue"; +import Spinner from "../../shared/Spinner.vue"; +import ExamplesComponent from "../shared/frame-components/ExamplesComponent.vue"; +import SlowalFrameComponent from "../shared/frame-components/SlowalFrameComponent.vue"; +import SemanticsSchemataComponent from "../shared/frame-components/SemanticsSchemataComponent.vue"; +import MeaningComponent from "../shared/frame-components/MeaningComponent.vue"; +import SelectionalPreference from "../Unification/SelectionalPreference.js"; +import { slowal_frames2selecional_preferencies } from "../shared/utils.js"; +import FreeLuPreview from "./FreeLuPreview.vue"; +import FreeLuElement from "./FreeLuElement.vue"; + +let FreeLuEdit = { + components: {FreeLuElement, FreeLuPreview} +}; + +Object.assign(FreeLuEdit, { + props: { + unifiedFrameId: Number, + previewedUnifiedFrameId: Number, + readOnly: Boolean, + initialRightPaneTab: String, + forceRefresh: Number, + }, + data() { + return { + gettext: window.gettext, + FreeLuEdit: FreeLuEdit, + unified_frame: {}, + unified_frame_title: '', + unified_frame_arguments: [], + active_unified_frame_argument: null, + slowal_frames2selecional_preferencies_mapping: {}, + lexical_units: [], + img_prefix: window.STATIC_URL, + frames: [], + right_pane_tabs: [ + {id: 'schemata', label: gettext('Schematy')}, + {id: 'frame_preview', label: gettext('PodglÄ…d ram')}, + {id: 'hierarchy', label: gettext('Hierarchia')}, + {id: 'notes', label: gettext('Notatki')}, + ], + right_pane_tab: this.initialRightPaneTab || 'schemata', + currentPreviewedUnifiedFrameId: this.previewedUnifiedFrameId, + internalForceRefresh: this.forceRefresh, + statusButtonTitle: '', + active_slowal_frame: null, + showVerifiedFrames: false, + subentries: null, + alternations: null, + realisation_phrases: null, + realisation_descriptions: null, + examples: null, + selectedFrameArguments: null, + frame_arguments_or_type: false, + selectedLus: null, + selectedSchemas: null, + selectedExamples: null, + hidden_frames: [], + hierarchy_hyponyms: null, + hierarchy_hyperonyms: null, + } + }, + components: {FreeLuElement, InfoTooltip, Spinner, FreeLuPreview, SlowalFrameComponent, ExamplesComponent, SemanticsSchemataComponent, MeaningComponent}, + emits: ['goToDisplay', 'refresh', 'swapFrames', 'refreshEntriesList', 'clearUnifiedFrameView'], + watch: { + forceRefresh(newVal, oldVal) { + this.loadFrame(); + } + }, + computed: { + selectionalPreference() { + return new SelectionalPreference(); + }, + }, + methods: { + createHierarchyTree() { + return { + 'unified_frame': this.unified_frame, + 'hyponyms': this.hierarchy_hyponyms, + 'hypyronyms': this.hierarchy_hyperonyms + }; + }, + goToEdit (unifiedFrameId, entryId, lexicalUnitId) { + window.location = `/${lang}/entries/unification?unified_frame_id=${this.unified_frame.id}`; + }, + hasWhiteSpace(s) { + return /\s/g.test(s); + }, + async loadFrame() { + try { + const data = {'unified_frame_id': this.unifiedFrameId, 'no_filters' : false}; + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_unified_frame/', + dataType: 'json', + data: data, + timeout: 60000, + success: function (response) { + + this.img_prefix = window.STATIC_URL; + this.lexical_units = this.frames2lexical_units(response.frames); + this.unified_frame = response.unified_frame; + this.unified_frame_title = this.unified_frame.title; + this.unified_frame_arguments = this.unified_frame.arguments; + this.frames = response.frames; + this.slowal_frames2selecional_preferencies_mapping = slowal_frames2selecional_preferencies(this.unified_frame, response.frames); + + this.subentries = response.subentries; + this.alternations = response.alternations; + this.realisation_phrases = response.realisation_phrases; + this.realisation_descriptions = response.realisation_descriptions; + this.examples = response.examples; + + this.fulfill_slowal_frames_arguments_with_empty_elems(response.unified_frame, response.frames) + window.update_last_visited(response.last_visited); + window.clear_info(); + + if (!this.active_slowal_frame) { + this.setup_notes_unified_frame(); + } + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + this.loadHierarchy(); + } catch (error) { + console.log(error); + } + }, + loadHierarchy() { + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyponyms/'+this.unifiedFrameId+"/", + dataType: 'json', + timeout: 60000, + success: function (response) { + this.hierarchy_hyponyms = response.hyponyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyperonyms/'+this.unifiedFrameId+"/", + dataType: 'json', + timeout: 60000, + success: function (response) { + this.hierarchy_hyperonyms = response.hyperonyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + }, + setup_notes_unified_frame() { + setup_notes($('#notes-component'), $('#lexical-unit-notes-template'), this.unified_frame.id, 'unifier.UnifiedFrame', + this.setup_notes_unified_frame, 'hierarchy_frame', true); + }, + unifiedFrameArgumentSelected(argument) { + if (this.active_unified_frame_argument === argument) { + this.active_unified_frame_argument = null; + this.frame_arguments_or_type = false; + this.deselectSlowalFrameSelectedElements(); + } else { + this.active_slowal_frame = null; + this.active_unified_frame_argument = argument; + const slowalFrameArguments = this.getSlowalFrameArgumentsBy(argument); + this.frame_arguments_or_type = true; + this.deselectSlowalFrameSelectedElements(); + this.selectedFrameArguments = slowalFrameArguments; + } + this.unifiedFrameArgumentHovered(argument); + }, + unifiedFrameArgumentHovered(argument) { + clear_info(); + if (argument && this.active_unified_frame_argument === argument) { + show_info(gettext('Kliknij, aby cofnąć wybór kolumny do edycji.')); + } + if (argument && this.active_unified_frame_argument !== argument) { + show_info(gettext('Kliknij, aby wybrać kolumnÄ™ do edycji.')); + } + }, + create_hyponym_or_hyperonym_relation(isHyperonym) { + if (this.currentPreviewedUnifiedFrameId === -1) { + alert(gettext("Wybierz w oknie podglÄ…du ram ramÄ™, która bÄ™dzie " + (isHyperonym ? "hiperonimem" : "hiponimem") + " ramy z okna głównego.")); + } else { + const data = isHyperonym ? {'hyponym_id': this.currentPreviewedUnifiedFrameId, 'hyperonym_id': this.unified_frame.id} : + {'hyponym_id': this.unified_frame.id, 'hyperonym_id': this.currentPreviewedUnifiedFrameId};; + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/hierarchy_assign/', + dataType: 'json', + data: data, + timeout: 60000, + success: function (response) { + if(response.succ === 'true') { + show_info('Relacja hierarchii zostaÅ‚a ustanowiona.'); + this.loadHierarchy(); + this.$emit('refreshEntriesList'); + } else if(response.exists === 'true') { + alert(gettext("Wybrana relacja hierarchii już wystÄ™puje.")); + } + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + } + }, + delete_hyponym_or_hyperonym_relation() { + if (this.currentPreviewedUnifiedFrameId === -1) { + alert(gettext("Wybierz w oknie podglÄ…du ram ramÄ™, dla której powiÄ…zanie ma zostać usuniÄ™te.")); + } else { + const data = {'rel1_id': this.currentPreviewedUnifiedFrameId, 'rel2_id': this.unified_frame.id}; + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/hierarchy_unassign/', + dataType: 'json', + data: data, + timeout: 60000, + success: function (response) { + if(response.succ === true) { + show_info('Relacja hierarchii zostaÅ‚a usuniÄ™ta.'); + this.loadHierarchy(); + this.$emit('refreshEntriesList'); + } else { + alert(gettext("Wybrana relacja hierarchii nie wystÄ™puje.")); + } + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + } + }, + getSlowalFrameArgumentsBy(unified_frame_argument) { + const slowalFrameArgumentIds = []; + for (let i in this.unified_frame.slowal_frame_mapping) { + const slowal_frame_mapping = this.unified_frame.slowal_frame_mapping[i]; + const slowalFrame = this.frames.find(frame => frame.id === slowal_frame_mapping.slowal_frame_id); + if(slowalFrame != null) { + for (let j in slowal_frame_mapping.slowal_frame_argument_mapping) { + const slowal_frame_argument_mapping = slowal_frame_mapping.slowal_frame_argument_mapping[j]; + if (slowal_frame_argument_mapping.unified_frame_agrument_id == unified_frame_argument.id) { + const slowalFrameArgument = slowalFrame.arguments.find(arg => arg.argument_id === slowal_frame_argument_mapping.slowal_frame_agrument_id); + slowalFrameArgumentIds.push(slowalFrameArgument); + } + } + } + } + return slowalFrameArgumentIds; + }, + isSuperLeksykograf() { + return has_permission("users.view_assignment"); + }, + isFrameVerified(frame) { + const isSuperLeksykograf = this.isSuperLeksykograf(); + return (!isSuperLeksykograf && frame.status === 'G') || (isSuperLeksykograf && frame.status === 'S') + }, + select_slowal_frame_req(to_invoke) { + if (this.active_slowal_frame) { + to_invoke(); + } else { + alert(gettext("Wybierz ramÄ™, dla której chcesz zmienić status.")); + } + }, + isFrameVisible(status) { + return (status != 'B' && status != 'C') || this.isSuperLeksykograf(); + }, + deselectSlowalFrameSelectedElements() { + this.subentries.forEach(subentry => { + subentry.schemata.forEach(s => { + s.selected = false; + }); + }); + this.frames.forEach(frame => { + frame.lexical_units.forEach(lu => { + lu.selected = false; + }); + frame.arguments.forEach(argument => { + argument.selected = false; + }); + }); + this.examples.forEach(example => { + example.selected = false; + }); + this.selectedLus = []; + this.selectedFrameArguments = []; + this.selectedSchemas = []; + this.selectedExamples = []; + }, + slowalFrameSelected(frame) { + this.deselectSlowalFrameSelectedElements(); + if (this.active_slowal_frame === frame) { + this.active_slowal_frame = null; + } else { + this.active_slowal_frame = frame; + } + }, + isSelectedFrame(frame) { + if (this.active_slowal_frame) { + return frame.id === this.active_slowal_frame.id; + } else { + return false; + } + }, + changePreviewedUnifiedFrameId(unifiedFrameId) { + this.currentPreviewedUnifiedFrameId = unifiedFrameId; + }, + getArgumentCSS(argument) { + return (argument.role ? argument.role.str + ' ' : '') + (argument == this.active_unified_frame_argument ? 'active' : ''); + }, + schemataSelected(schemas) { + this.selectedSchemas = schemas; + }, + exampleSelected(selectedExamples) { + this.selectedExamples = selectedExamples; + }, + isReadOnlyForSuperLeksykograf() { + return (this.isSuperLeksykograf() && this.unified_frame.status === 'O') && this.unified_frame.assignee_username !== window.USER_USERNAME; + }, + frames2lexical_units(frames) { + const lexical_units = [] + for (let i in frames) { + const frame = frames[i]; + for (let j in frame.lexical_units) { + const lexical_unit = frame.lexical_units[j]; + lexical_unit.opinion = frame.opinion; + lexical_unit.opinion_key = frame.opinion_key; + lexical_unit.frame_status = frame.status; + lexical_unit.frame = frame; + lexical_units.push(lexical_unit); + } + } + return lexical_units; + }, + fulfill_slowal_frames_arguments_with_empty_elems(unified_frame, slowal_frames) { + for (let i in unified_frame.slowal_frame_mapping) { + const slowal_frame_mapping = unified_frame.slowal_frame_mapping[i]; + let slowal_frame = slowal_frames.find(o => o.id === slowal_frame_mapping.slowal_frame_id); + if(slowal_frame != null) { + let new_slowal_frame_arguments = []; + for (let j in unified_frame.arguments) { + const unified_frame_argument = unified_frame.arguments[j]; + let unified_frame_argument_mapping = slowal_frame_mapping.slowal_frame_argument_mapping.find(o => o.unified_frame_agrument_id === unified_frame_argument.id); + let slowal_frame_argument = null; + if (unified_frame_argument_mapping == null) { + slowal_frame_argument = { + 'str': 'Empty', + 'id': slowal_frame.id + '-_' + (unified_frame_argument.id), + 'role': 'Empty', + 'role_type': 'Empty', + 'preferences': [], + 'proposed_roles': [], + } + } else { + slowal_frame_argument = slowal_frame.arguments.find(o => o.argument_id === unified_frame_argument_mapping.slowal_frame_agrument_id); + } + new_slowal_frame_arguments.push(slowal_frame_argument) + } + slowal_frame.arguments = new_slowal_frame_arguments; + } + } + }, + createFrameHierarchyRepresentationHTML(unified_frame) { + + }, + createHierarchyHTML() { + this.unified_frame + } + }, + mounted() { + + if(this.unifiedFrameId) { + this.loadFrame(); + } + + if(!this.readOnly) { + Split(['#semantics-frames-pane', '#semantics-schemata-pane'], { + sizes: [40, 60], + minSize: 10, + gutterSize: 4, + elementStyle: (dimension, size, gutterSize) => { + return { + 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' + } + }, + }); + Split(['#semantics-unified-frame-pane', '#examples'], { + sizes: [60, 40], + direction: 'vertical', + gutterSize: 4, + minSize: 10, + }); + } else { + Split(['#semantics-unified-frame-pane-preview', '#examples-preview'], { + sizes: [60, 40], + direction: 'vertical', + gutterSize: 4, + minSize: 10, + }); + } + } +}); + +export default FreeLuEdit; + +</script> + +<template> + <div class="col h-100 px-0 pt-0 pb-0 overflow-auto" id="semantics-frames-pane"> + <div :id="'semantics-unified-frame-pane' + (readOnly ? '-preview' : '')" class="col w-100 p-0 overflow-auto"> + <table v-if="!readOnly && !isReadOnlyForSuperLeksykograf()" class="table-button-menu sticky-top" cellspacing="1"> + <tr style="background-color: white;"> + <td id="change-title" @click="create_hyponym_or_hyperonym_relation(false)" style="padding: 10px 15px 10px 15px; color: #000000;">Nadrama</td> + <td id="add-arg" @click="create_hyponym_or_hyperonym_relation(true)" style="padding: 10px 15px 10px 15px; color: #000000;">Podrama</td> + <td style="padding: 10px 15px 10px 15px; color: #000000;" @click="delete_hyponym_or_hyperonym_relation">UsuÅ„ powiÄ…zanie</td> + <td style="padding: 10px 15px 10px 15px; color: #000000;" @click="goToEdit">Edytuj</td> + </tr> + </table> + + <spinner /> + <div align="center"> + <div align="left" style="display: table;"> + <div class="unifiedFrame mt-3" v-bind:data-frame_id="unified_frame.id" id="unified-frame-title" v-html="unified_frame_title"></div> + <table v-if="unified_frame.id" id="unified-frame" class="m-0 table-borderless border border-secondary text-dark frame active"> + <tbody> + <tr> + <template v-for="argument in unified_frame_arguments"> + <td + class="argument py-2 px-1 border-top border-left border-secondary role-column" + :class="getArgumentCSS(argument)" + @click="unifiedFrameArgumentSelected(argument)" + @mouseover="unifiedFrameArgumentHovered(argument)" + @mouseleave="unifiedFrameArgumentHovered(null)" + > + {{ argument.role_type }} + + <div + v-if="argument.role" + > + [{{ argument.role.str }}] + </div> + <div v-else> + <ul class="ul-role"> + <li v-for="proposed_role in argument.proposed_roles"> + {{ proposed_role.str }} + </li> + </ul> + </div> + </td> + </template> + + + </tr> + <tr> + <td class="preferences py-0 px-0 border-top border-left border-secondary role-column align-top" + v-for='argument in unified_frame_arguments' + :key='argument.id' + > + <ul class="ul-preference" v-if="argument.preferences.length > 0"> + <li v-for='preference in argument.preferences'> + <div + v-if="preference.url != null" + class="preference py-2 px-1 preference-bold" + > + <a class="synset-plwn" v-bind:href="preference.url" target="_blank">{{ preference.str }}</a> + </div> + <div v-else class="preference py-2 px-1 preference-bold">{{ preference.str }}</div> + </li> + </ul> + <ul class="ul-preference" v-if="unified_frame.status !== 'S'"> + <li v-for="preference in slowal_frames2selecional_preferencies_mapping[argument.id]"> + <span v-if="preference.url != null" class="preference py-2 px-1"> + <a class="synset-plwn" v-bind:href="preference.url" target="_blank">{{ preference.str }}</a> + </span> + <span v-else class="preference py-2 px-1">{{ preference.str }}</span> + <info-tooltip v-if="preference.info" :text="preference.info" /> + </li> + </ul> + </td> + </tr> + </tbody> + </table> + </div> + <div v-if="unified_frame.id" class="lu-table mt-3 mb-3"> + <table class="m-0 table-borderless border border-secondary text-dark"> + <tbody> + <tr> + <th scope="row" class="py-2 px-1 text-secondary">Jednostka leksykalna</th> + <th scope="row" class="py-2 px-1 text-secondary">Opinia</th> + <th scope="row" class="py-2 px-1 text-secondary">Status</th> + </tr> + <tr class="preferences py-0 px-0 border-top border-left border-secondary" + v-for='lexical_unit in lexical_units' + > + <template v-if="isFrameVisible(lexical_unit.frame)"> + <td class="cursor-pointer argument py-2 px-1 border-top border-left border-secondary" + @mouseenter="lexical_unit.frame.hover=true" + @mouseleave="lexical_unit.frame.hover=false" + :class="lexical_unit.frame === active_slowal_frame ? 'active-lu' : lexical_unit.frame.hover ? 'lu-underline' : ''" + @click="slowalFrameSelected(lexical_unit.frame)">{{ lexical_unit.str }}</td> + <td class="argument py-2 px-1 border-top border-left border-secondary"> + <img v-bind:src="img_prefix + 'entries/img/' +lexical_unit.opinion_key + '.svg'" width="12" height="12" v-bind:alt="lexical_unit.opinion"> + {{ lexical_unit.opinion }} + </td> + <td class="argument py-2 px-1 border-top border-left border-secondary">[{{ lexical_unit.frame_status }}]</td> + </template> + </tr> + </tbody> + </table> + </div> + </div> + </div> + <div :id="'examples' + (readOnly ? '-preview' : '')" class="col w-100 p-0 tab-pane overflow-auto"> + <examples-component v-if="examples" + :examples="examples" + :frame="active_slowal_frame" + :frame_arguments="selectedFrameArguments" + :frame_arguments_or_type="frame_arguments_or_type" + :lus="selectedLus" + :schemas="selectedSchemas" + :key="examples" + @example-selected="exampleSelected" + /> + </div> + </div> + <div v-if="!readOnly" class="col h-100 px-1 pt-0 pb-0 overflow-auto" style="padding-left: 0px!important; padding-right: 0px!important;" id="semantics-schemata-pane"> + <ul class="nav nav-pills nav-justified p-1" id="entryTabs" role="tablist"> + <li + v-for="tab in right_pane_tabs" + class="btn btn-sm btn-outline-dark nav-link mx-1" + :class="right_pane_tab === tab.id && 'active'" + @click="right_pane_tab = tab.id" + > + {{ tab.label }} + </li> + </ul> + <div v-if="active_slowal_frame" :class="right_pane_tab !== 'schemata' && 'd-none'"> + <semantics-schemata-component + :subentries="subentries" + :key="subentries" + :frame="active_slowal_frame" + :alternations="alternations" + :realisation_phrases="realisation_phrases" + :realisation_descriptions="realisation_descriptions" + :selectedExamples="selectedExamples" + @schemata-selected="schemataSelected" + /> + </div> + </div> +</template> diff --git a/frontend/src/components/unification/free_lu/FreeLuElement.vue b/frontend/src/components/unification/free_lu/FreeLuElement.vue new file mode 100644 index 0000000..5a2b4aa --- /dev/null +++ b/frontend/src/components/unification/free_lu/FreeLuElement.vue @@ -0,0 +1,188 @@ +<script> + +export default { + name: 'FreeLuElement', + props: { + node: { + type: Object, + required: true + }, + spacing: { + type: Number, + default: 0 + }, + spacing_elem_type: String + }, + data() { + return { + showHyponyms: false, + showHypyronyms: false + } + }, + computed: { + nodeMargin() { + return { + 'margin-left': `${this.spacing}px` + } + }, + hasHyponyms() { + const {hyponyms} = this.node + return hyponyms && hyponyms.length > 0 + }, + hasHypyronyms() { + const {hypyronyms} = this.node + return hypyronyms && hypyronyms.length > 0 + }, + toggleChildrenIcon() { + return this.showHypyronyms ? 'fas fa-angle-down' : 'fas fa-angle-right' + }, + getSpacingElem() { + if(this.spacing_elem_type === "std") { + return "│ ├→ │"; + } else if(this.spacing_elem_type === "top_corner") { + return "        ┌→ │ │"; + } else if(this.spacing_elem_type === "down_corner") { + return "│ │ └→"; + } else { + return ""; + } + }, + }, + methods: { + loadChildren (children) { + for (let i in children) { + const hypyronym = children[i]; + if (hypyronym.hasChildrenLoaded !== true) { + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyperonyms/' + hypyronym.unified_frame_id + "/", + dataType: 'json', + timeout: 60000, + success: function (response) { + hypyronym.hypyronyms = response.hyperonyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + $.ajax({ + type: 'post', + url: '/' + lang + '/unifier/get_hierarchy_hyponyms/' + hypyronym.unified_frame_id + "/", + dataType: 'json', + timeout: 60000, + success: function (response) { + hypyronym.hyponyms = response.hyponyms; + }.bind(this), + error: function (request, errorType, errorMessage) { + show_error(errorType + ' (' + errorMessage + ')'); + } + }); + } + } + }, toggleHypyronyms() { + this.loadChildren(this.node.hypyronyms); + this.showHypyronyms = !this.showHypyronyms + }, + toggleHyponyms() { + this.loadChildren(this.node.hyponyms); + this.showHyponyms = !this.showHyponyms + } + } +} + +</script> + + +<template> + <div :style="nodeMargin" style="height: 100%"> + <div v-if="hasHypyronyms" v-show="showHypyronyms"> + <free-lu-element + v-for="(child, index) in node.hypyronyms" + :key="child.id" + :node="child" + :spacing_elem_type='index === 0 ? "top_corner" : "std"' + :spacing="spacing + 40" + /> + </div> + <div show class="d-flex justify-content-between mb-1"> + <div class="row"> + <div class="col" style="max-width: 10px" v-html="getSpacingElem"/> + <div class="col"> + <div class="row"> + <div class="col cursor-pointer" style="text-align: center; font-size: large"><span + v-if="hasHypyronyms" + :class="toggleChildrenIcon" + @click="toggleHypyronyms" + @keypress="toggleHypyronyms">▲</span></div> + </div> + <div class="row"> + <div class="col" align="left" style="display: table;"> + <div class="unifiedFrame" id="hierarchy-unified-frame-title" v-html="node.unified_frame.title"></div> + <table id="hierarchy-unified-frame" class="m-0 table-borderless border border-secondary text-dark frame active"> + <tbody> + <tr> + <template v-for="argument in node.unified_frame.arguments"> + <td + class="argument py-2 px-1 border-top border-left border-secondary role-column" + :class="argument.role ? argument.role.str + ' ' : ''" + > + {{ argument.role_type }} + + <div + v-if="argument.role" + > + [{{ argument.role.str }}] + </div> + <div v-else> + <ul class="ul-role"> + <li v-for="proposed_role in argument.proposed_roles"> + {{ proposed_role.str }} + </li> + </ul> + </div> + </td> + </template> + </tr> + <tr> + <td hidden class="preferences py-0 px-0 border-top border-left border-secondary role-column align-top" + v-for='argument in node.unified_frame.arguments' + :key='argument.id' + > + <ul class="ul-preference" v-if="argument.preferences.length > 0"> + <li v-for='preference in argument.preferences'> + <div + v-if="preference.url != null" + class="preference py-2 px-1 preference-bold" + > + <a class="synset-plwn" v-bind:href="preference.url" target="_blank">{{ preference.str }}</a> + </div> + <div v-else class="preference py-2 px-1 preference-bold">{{ preference.str }}</div> + </li> + </ul> + </td> + </tr> + </tbody> + </table> + </div> + </div> + <div class="row align-content-center"> + <div class="col cursor-pointer" style="text-align: center; font-size: large"><span + v-if="hasHyponyms" + :class="toggleChildrenIcon" + @click="toggleHyponyms" + @keypress="toggleHyponyms">▼</span></div> + </div> + </div> + </div> + </div> + <div v-if="hasHyponyms" v-show="showHyponyms"> + <free-lu-element + v-for="(child, index) in node.hyponyms" + :key="child.id" + :node="child" + :spacing_elem_type='index === node.hyponyms.length - 1 ? "down_corner" : "std"' + :spacing="spacing + 40" + /> + </div> + </div> +</template> diff --git a/frontend/src/components/unification/free_lu/FreeLuPreview.vue b/frontend/src/components/unification/free_lu/FreeLuPreview.vue new file mode 100644 index 0000000..a88db17 --- /dev/null +++ b/frontend/src/components/unification/free_lu/FreeLuPreview.vue @@ -0,0 +1,64 @@ +<script> +import UnificationFramesList from "../Unification/UnificationFramesList.vue"; + +export default { + props: { + initialUnifiedFrameId: Number, + initialLexicalUnitId: Number, + forceRefresh: Number, + hierarchyEditComponent: Object, + }, + data () { + return { + unifiedFrameId: this.initialUnifiedFrameId + }; + }, + components: {UnificationFramesList}, + emits: ['changeFrame', 'refreshEntriesList'], + methods: { + unifiedFrameSelected (unifiedFrameId) { + this.$emit('changeFrame', unifiedFrameId); + }, + refreshEntriesList() { + this.$emit('refreshEntriesList') + }, + }, + mounted () { + Split(['#frame-preview-left-pane', '#frame-preview-right-pane'], { + sizes: [60, 40], + minSize: 20, + gutterSize: 4, + elementStyle: (dimension, size, gutterSize) => { + return { + 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' + } + }, + }); + } +}; +</script> + +<template> + <div class="row h-100 overflow-hidden"> + <div :key="unifiedFrameId" class="col h-100 pr-0 pt-0 pb-0 overflow-auto" id="frame-preview-left-pane"> + <component v-bind:is="hierarchyEditComponent" + v-if="unifiedFrameId !== -1" + :readOnly="true" + :unifiedFrameId="unifiedFrameId" + :forceRefresh="forceRefresh" + @refresh-entries-list="refreshEntriesList" + /> + <div v-else class="h-100"> + Brak ramy do wyÅ›wietlenia + </div> + </div> + <div class="col h-100 pl-1 pt-0 pb-0 overflow-auto" id="frame-preview-right-pane"> +<!-- <unification-switchable-list--> +<!-- @unified-frame-selected="unifiedFrameSelected"--> +<!-- />--> + <unification-frames-list + @unified-frame-selected="unifiedFrameSelected" + /> + </div> + </div> +</template> diff --git a/frontend/src/components/unification/free_lu/FreeLuRightPane.vue b/frontend/src/components/unification/free_lu/FreeLuRightPane.vue new file mode 100644 index 0000000..8372acd --- /dev/null +++ b/frontend/src/components/unification/free_lu/FreeLuRightPane.vue @@ -0,0 +1,68 @@ +<script> +import FreeLuEdit from './FreeLuEdit.vue'; + +export default { + components: {FreeLuEdit}, + props: { + entryId: Number, + lexicalUnitId: Number, + initialUnifiedFrameId: Number, + }, + emits: ['refreshEntriesList'], + data() { + return this.getInitialData(); + }, + methods: { + getInitialData() { + return { + key: this.lexicalUnitId, + entryIdLocal: this.entryId, + unifiedFrameId: this.initialUnifiedFrameId, + previewedUnifiedFrameId: -1 + }; + }, + goToDisplay() { + this.unifiedFrameId = null; + }, + refresh() { + this.key = null; + setTimeout(() => { + this.key = this.lexicalUnitId; + }, 0); + }, + swapFrames(previewedUnifiedFrameId) { + this.previewedUnifiedFrameId = this.unifiedFrameId; + this.unifiedFrameId = previewedUnifiedFrameId; + this.refresh(); + }, + refreshEntriesList() { + this.$emit('refreshEntriesList'); + } + }, + watch: { + lexicalUnitId() { + Object.assign(this, this.getInitialData()); + }, + initialUnifiedFrameId() { + Object.assign(this, this.getInitialData()); + } + }, +}; +</script> + +<template> + <div v-if="key || unifiedFrameId" :key="(key, entryIdLocal, unifiedFrameId)" class="row h-100 m-0 p-0 overflow-auto" id="semantics-top-pane"> + <free-lu-edit + ref="hierarchyEdit" + v-if="unifiedFrameId" + :key="unifiedFrameId" + :readOnly="false" + :unifiedFrameId="unifiedFrameId" + :previewedUnifiedFrameId="previewedUnifiedFrameId" + :initialRightPaneTab="previewedUnifiedFrameId && unifiedFrameId !== previewedUnifiedFrameId ? 'frame_preview' : 'schemata'" + @go-to-display="goToDisplay" + @swap-frames="swapFrames" + @refresh-entries-list="refreshEntriesList" + /> + </div> +</template> diff --git a/frontend/src/main.js b/frontend/src/main.js index 21c30df..6162fa0 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -6,6 +6,7 @@ import { createRouter, createWebHistory } from "vue-router"; import Entries from "./components/unification/Entries/Entries.vue"; import Unification from "./components/unification/Unification/Unification.vue"; import Hierarchy from "./components/unification/hierarchy/Hierarchy.vue"; +import FreeLu from "./components/unification/free_lu/FreeLu.vue"; const router = createRouter({ history: createWebHistory(), @@ -13,6 +14,7 @@ const router = createRouter({ { path: '/:lang/entries/', component: Entries }, { path: '/:lang/entries/unification/', component: Unification }, { path: '/:lang/entries/hierarchy/', component: Hierarchy }, + { path: '/:lang/entries/lu_free/', component: FreeLu }, { path: '/:pathMatch(.*)*', component: null, name: '404' }, ] }); -- GitLab