kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
5.3 KiB
149 lines
5.3 KiB
import {$injectFields} from '../angular/angular-injector-bridge.functions';
|
|
import {WorkPackageDisplayFieldService} from '../wp-display/wp-display-field/wp-display-field.service';
|
|
import {WorkPackageResourceInterface} from '../api/api-v3/hal-resources/work-package-resource.service';
|
|
import {DisplayField} from '../wp-display/wp-display-field/wp-display-field.module';
|
|
import {MultipleLinesStringObjectsDisplayField} from '../wp-display/field-types/wp-display-multiple-lines-string-objects-field.module';
|
|
import {WorkPackageChangeset} from './work-package-changeset';
|
|
|
|
export const editableClassName = '-editable';
|
|
export const requiredClassName = '-required';
|
|
export const readOnlyClassName = '-read-only';
|
|
export const placeholderClassName = '-placeholder';
|
|
export const cellClassName = 'wp-table--cell-span';
|
|
export const displayClassName = 'wp-edit-field--display-field';
|
|
export const editFieldContainerClass = 'wp-edit-field--container';
|
|
export const cellEmptyPlaceholder = '-';
|
|
|
|
export class DisplayFieldRenderer {
|
|
|
|
public wpDisplayField:WorkPackageDisplayFieldService;
|
|
public I18n:op.I18n;
|
|
|
|
constructor(public context:'table' | 'single-view') {
|
|
$injectFields(this, 'wpDisplayField', 'I18n');
|
|
}
|
|
|
|
public render(workPackage:WorkPackageResourceInterface,
|
|
name:string,
|
|
changeset:WorkPackageChangeset|null,
|
|
placeholder = cellEmptyPlaceholder):HTMLSpanElement {
|
|
const [field, span] = this.renderFieldValue(workPackage, name, changeset, placeholder);
|
|
|
|
if (field === null) {
|
|
return span;
|
|
}
|
|
|
|
this.setSpanAttributes(span, field, name, workPackage);
|
|
|
|
return span;
|
|
}
|
|
|
|
public renderFieldValue(workPackage:WorkPackageResourceInterface,
|
|
name:string,
|
|
changeset:WorkPackageChangeset|null,
|
|
placeholder = cellEmptyPlaceholder):[DisplayField|null, HTMLSpanElement] {
|
|
const span = document.createElement('span');
|
|
const schemaName = workPackage.getSchemaName(name);
|
|
const fieldSchema = workPackage.schema[schemaName];
|
|
|
|
// If the work package does not have that field, return an empty
|
|
// span (e.g., for the table).
|
|
if (!fieldSchema) {
|
|
return [null, span];
|
|
}
|
|
|
|
const field = this.getField(workPackage, fieldSchema, schemaName, changeset);
|
|
field.render(span, this.getText(field, placeholder));
|
|
span.setAttribute('title', this.getLabel(field, workPackage));
|
|
span.setAttribute('aria-label', this.getAriaLabel(field, workPackage));
|
|
|
|
return [field, span];
|
|
}
|
|
|
|
public getField(workPackage:WorkPackageResourceInterface,
|
|
fieldSchema:op.FieldSchema,
|
|
name:string,
|
|
changeset:WorkPackageChangeset|null):DisplayField {
|
|
// We handle multi value fields differently in the single view context
|
|
const isMultiLinesField = ['[]CustomOption', '[]User'].indexOf(fieldSchema.type) >= 0;
|
|
let field:DisplayField;
|
|
if (this.context === 'single-view' && isMultiLinesField) {
|
|
field = new MultipleLinesStringObjectsDisplayField(workPackage, name, fieldSchema) as DisplayField;
|
|
} else {
|
|
field = this.wpDisplayField.getField(workPackage, name, fieldSchema) as DisplayField;
|
|
}
|
|
field.changeset = changeset;
|
|
|
|
return field;
|
|
}
|
|
|
|
private getText(field:DisplayField, placeholder:string):string {
|
|
if (field.isEmpty()) {
|
|
return placeholder;
|
|
} else {
|
|
return field.valueString;
|
|
}
|
|
}
|
|
|
|
private setSpanAttributes(span:HTMLElement, field:DisplayField, name:string, workPackage:WorkPackageResourceInterface):void {
|
|
span.classList.add(cellClassName, displayClassName, 'inplace-edit', 'wp-edit-field', name);
|
|
span.dataset['fieldName'] = name;
|
|
|
|
// Make span tabbable unless it's an id field
|
|
span.setAttribute('tabindex', name === 'id' ? '-1' : '0');
|
|
|
|
if (field.required) {
|
|
span.classList.add(requiredClassName);
|
|
}
|
|
|
|
if (field.isEmpty()) {
|
|
span.classList.add(placeholderClassName);
|
|
}
|
|
|
|
if (field.writable && workPackage.isEditable) {
|
|
span.classList.add(editableClassName);
|
|
span.setAttribute('role', 'button');
|
|
} else {
|
|
span.classList.add(readOnlyClassName);
|
|
}
|
|
}
|
|
|
|
private getLabel(field:DisplayField, workPackage:WorkPackageResourceInterface):string {
|
|
if (field.writable && workPackage.isEditable) {
|
|
return this.I18n.t('js.inplace.button_edit', { attribute: `${field.displayName}` });
|
|
} else {
|
|
return field.displayName;
|
|
}
|
|
}
|
|
|
|
private getAriaLabel(field:DisplayField, workPackage:WorkPackageResourceInterface):string {
|
|
let titleContent;
|
|
let labelContent = this.getLabelContent(field);
|
|
|
|
if (field.isFormattable && !field.isEmpty()) {
|
|
try {
|
|
titleContent = _.escape(angular.element(`<div>${labelContent}</div>`).text());
|
|
} catch(e) {
|
|
console.error("Failed to parse formattable labelContent");
|
|
titleContent = "Label for " + field.displayName;
|
|
}
|
|
|
|
} else {
|
|
titleContent = labelContent;
|
|
}
|
|
|
|
if (field.writable && workPackage.isEditable) {
|
|
return this.I18n.t('js.inplace.button_edit', { attribute: `${field.displayName} ${titleContent}` });
|
|
} else {
|
|
return `${field.displayName} ${titleContent}`;
|
|
}
|
|
}
|
|
|
|
private getLabelContent(field:DisplayField):string {
|
|
if (field.isEmpty()) {
|
|
return this.I18n.t('js.inplace.null_value_label');
|
|
} else {
|
|
return field.valueString;
|
|
}
|
|
}
|
|
}
|
|
|