Skip to content

Commit

Permalink
feat(tag note): load type att dynamically and allow for custom input
Browse files Browse the repository at this point in the history
Type options are pulled from the schema. If the schema doesn't define the options, the default
options will be displayed. If the schema allows, the user can insert a custom type, or have no type
at all. The interface was changed from radiobox to dropdown to improove ux. A popup window shows up
when space is hit while typing a custom type warning about this type of action on xml att.

BREAKING CHANGE: options are pulled dinamically. Checks need to be done while building the dialog.

#274
  • Loading branch information
lucaju committed Jul 9, 2020
1 parent 0092c04 commit b4a943e
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 61 deletions.
33 changes: 19 additions & 14 deletions src/js/dialogs/dialogForm/dialogForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,18 @@ DialogForm.processForm = function(dialogInstance) {
mapping = mapping.replace(/^prop\./, '');
dataKey = 'properties';
}

let val;
switch (type) {
case 'radio':
var val = formEl.find('input:checked').val();
val = formEl.find('input:checked').val();
data[dataKey][mapping] = val;
break;
case 'textbox':
case 'hidden':
case 'select':
var val = formEl.val();
data[dataKey][mapping] = val;
val = formEl.val();
if (val !== null) data[dataKey][mapping] = val;
break;
}
}
Expand Down Expand Up @@ -172,21 +174,24 @@ DialogForm.populateForm = function(dialogInstance) {
} else {
value = data.attributes[mapping];
}

if (mapping === 'otherType') value = data.attributes['type'];

if (value !== undefined) {
switch (type) {
case 'select':
const selectedOption = $(`option[value="${value}"]`, formEl);
if (!selectedOption[0]) value = 'other' //if there is no option for the value, select 'other' option
formEl.val(value);
if (formEl.data('transform') === 'selectmenu') {
formEl.selectmenu('refresh');
}
if (formEl.data('transform') === 'selectmenu') formEl.selectmenu('refresh');
formEl.parents('[data-transform="accordion"]').accordion('option', 'active', 0);
break;

case 'radio':
$('input[value="'+value+'"]', formEl).prop('checked', true);
if (formEl.data('transform') === 'buttonset') {
$('input', formEl).button('refresh');
}
let radioOption = $(`input[value="${value}"]`, formEl);
if (!radioOption[0]) radioOption = $('input[value="other"]', formEl); //if there is no option for the value, check 'other' option
radioOption.prop('checked', true);
if (formEl.data('transform') === 'buttonset') $('input', formEl).button('refresh');
break;
case 'textbox':
formEl.val(value);
Expand Down Expand Up @@ -228,6 +233,8 @@ DialogForm.prototype = {
}
this.attributesWidget.reset();
}

this.$el.trigger('buildDynamicFields', [config, this]);

// reset the form
$('[data-type]', this.$el).each(function(index, el) {
Expand Down Expand Up @@ -324,16 +331,15 @@ DialogForm.prototype = {
}

DialogForm.populateForm(this);

this.$el.trigger('beforeShow', [config, this]);

this.$el.dialog('open');
},

save: function() {
DialogForm.processForm(this);

this.$el.trigger('beforeSave', [this]);
DialogForm.processForm(this);

if (this.isValid === true) {
this.$el.trigger('beforeClose');
this.$el.dialog('close');
Expand All @@ -343,7 +349,6 @@ DialogForm.prototype = {
} else {
this.w.tagger.finalizeEntity(this.type, this.currentData);
}

this.$el.trigger('save', [this]);
}
},
Expand Down
181 changes: 134 additions & 47 deletions src/js/schema/tei/dialogs/note.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,143 @@
var $ = require('jquery');
var DialogForm = require('dialogForm');
const $ = require('jquery');
const DialogForm = require('dialogForm');

module.exports = function(writer, parentEl) {
var w = writer;

const defaultTypeOptions = [
{ value: 'researchNote', label: 'Research Note', title: 'Internal to projects' },
{ value: 'scholarNote', label: 'Scholarly Note', title: 'Footnotes/endnotes' },
{ value: 'annotation', label: 'Annotation', title: 'Informal notes' },
{ value: 'other', label: 'Other', title: 'Other Notes' },
];

module.exports = (writer, parentEl) => {

const type = 'note';

const atts = writer.schemaManager.getAttributesForTag(type);
const typeAtt = atts.find(({name}) => name === 'type');

const typeRequired = (typeAtt.required) ? 'required' : '';

var id = w.getUniqueId('noteForm_');
var $el = $(''+
'<div class="annotationDialog">'+
'<div>'+
'<div id="'+id+'_type" data-transform="buttonset" data-type="radio" data-mapping="type">'+
'<input type="radio" id="'+id+'_re" name="'+id+'_type" value="researchNote" data-default="true" /><label for="'+id+'_re" title="Internal to projects">Research Note</label>'+
'<input type="radio" id="'+id+'_scho" name="'+id+'_type" value="scholarNote" /><label for="'+id+'_scho" title="Footnotes/endnotes">Scholarly Note</label>'+
'<input type="radio" id="'+id+'_ann" name="'+id+'_type" value="annotation" /><label for="'+id+'_ann" title="Informal notes">Annotation</label>'+
'</div>'+
'</div>'+
'<div>'+
'<label for="'+id+'_noteContent">Note text</label>'+
'<textarea id="'+id+'_noteContent" data-type="textbox" data-mapping="prop.noteContent" style="width: 98%; height: 100px;"></textarea>'+
'<p>You will be able to tag and edit the text in the main document.</p>'+
'</div>'+
'<div data-transform="accordion">'+
'<h3>Markup options</h3>'+
'<div id="'+id+'_attParent" class="attributes" data-type="attributes" data-mapping="attributes">'+
'</div>'+
'</div>'+
'</div>').appendTo(parentEl);

var dialog = new DialogForm({
writer: w,
$el: $el,
width: 600,
height: 500,
type: 'note',
title: 'Tag Note'
const id = writer.getUniqueId('noteForm_');
const html = `
<div class="annotationDialog">
<div>
<label for="${id}_type"><b>Type</b></label>
<select id="${id}_type" name="${id}_type" data-type="select" data-mapping="type" ${typeRequired}></select>
<div id="${id}_noteOtherTypeSlot">
<br/>
<label for="${id}_noteOtherType"><b>Define Type</b></label>
<input type="text" id="${id}_noteOtherType" data-type="textbox" data-mapping="otherType" style="margin-right: 10px;"/>
</div>
</div>
<div>
<label for="${id}_noteContent">Note text</label>
<textarea id="${id}_noteContent" data-type="textbox" data-mapping="prop.noteContent" style="width: 98%; height: 100px;"></textarea>
<p>You will be able to tag and edit the text in the main document.</p>
</div>
<div data-transform="accordion">
<h3>Markup options</h3>
<div id="${id}_attParent" class="attributes" data-type="attributes" data-mapping="attributes">
</div>
</div>
`;

const $el = $(html).appendTo(parentEl);

const dialog = new DialogForm({
writer,
$el,
type,
title: 'Tag Note',
width: 600,
height: 500,
});

dialog.$el.on('beforeShow', function(e, config, dialog) {
if (dialog.mode === DialogForm.EDIT) {
dialog.$el.find('label[for='+id+'_noteContent]').hide();
dialog.$el.find('#'+id+'_noteContent').hide();
} else {
dialog.$el.find('label[for='+id+'_noteContent]').show();
dialog.$el.find('#'+id+'_noteContent').show();
const optionsTypeElement = dialog.$el.find(`#${id}_type`);
const noteOtherTypeElement = dialog.$el.find(`#${id}_noteOtherType`);

dialog.$el.on('buildDynamicFields', (e, config, dialog) => {
//TYPE
const typeChoices = (typeAtt.choices) ? typeAtt.choices : defaultTypeOptions;
const choiceOptions = generateTypeOptions(typeChoices);
optionsTypeElement.html(choiceOptions)
});

dialog.$el.on('beforeShow', (e, config, dialog) => {
const show = dialog.mode === DialogForm.EDIT;
dialog.$el.find(`label[for=${id}_noteContent]`).toggle(!show);
dialog.$el.find(`#${id}_noteContent`).toggle(!show);

//other type
const typeValue = optionsTypeElement.val();
const showOtherTypeTextFiel = (!typeAtt.choices && typeValue === 'other') ? true : false
toggleOtherTypeTextField(showOtherTypeTextFiel);

});

dialog.$el.on('beforeSave', (e, dialog) => {
//replace other type option for custom defined value
if (!typeAtt.choices && optionsTypeElement.val() === 'other') {
const otherTypeFieldValue= dialog.$el.find(`#${id}_noteOtherType`).val();
const typeCutstomOption = `<option value="${otherTypeFieldValue}" selected>${otherTypeFieldValue}</option>`;
optionsTypeElement.html(typeCutstomOption);
}
});

return {
show: function(config) {
dialog.show(config);
},
destroy: function() {
dialog.destroy();
}
//show/hide other type textfield
const toggleOtherTypeTextField = (show) => {
dialog.$el.find(`#${id}_noteOtherTypeSlot`).toggle(show);
if (!show) dialog.$el.find(`#${id}_noteOtherType`).val('');
};
};

//toggle other type text field
optionsTypeElement.change((e) => {
if (typeAtt.choices) return;
const target = $(e.target);
const otherTypeSelected = (target.val() === 'other') ? true : false;
toggleOtherTypeTextField(otherTypeSelected);
});

//transfer value from 'other type 'textfied to 'other' option value on radiobox
dialog.$el.find(`#${id}_noteOtherType`).change(() => {
const val = dialog.$el.find(`#${id}_noteOtherType`).val();
dialog.$el.find(`#${id}_other`).attr('value', val);
});


noteOtherTypeElement.keyup((e) => {
if (e.code === 'Space') {
writer.dialogManager.confirm({
title: 'Warning',
msg: 'You hit "space"',
height: 200,
type: 'info',
showConfirmKey: 'confirm-space-in-xml-values',
})
}
});

const generateTypeOptions = (choices) => {

let html = '<option value="" disabled selected hidden>Please Choose...</option>';
html += '<option value=""></option>';

choices.map((choice) => {
const value = (typeof choice === 'string') ? choice : choice.value;
const label = (typeof choice === 'string') ? choice : choice.label;

const defaultChoice = (typeAtt.defaultValue === value) ? true : false;
const selected = defaultChoice ? 'selected' : '';

html += `<option value="${value}" data-default="${defaultChoice}" ${selected}>${label}</option>`;
});

return html;
}

return {
show: (config) => dialog.show(config),
destroy: () => dialog.destroy(),
};
};
2 changes: 2 additions & 0 deletions src/js/tagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,8 @@ function Tagger(writer) {
var isNamedEntity = w.schemaManager.mapper.isNamedEntity(type);
var tagName = w.schemaManager.mapper.getParentTag(type);

if (type === 'note') delete info.attributes.otherType; //remove otherType attribute;

sanitizeObject(info.attributes, true);
sanitizeObject(info.customValues, false);

Expand Down

0 comments on commit b4a943e

Please sign in to comment.