Remove old Add/Edit Table Entity code (#755)
This commit is contained in:
parent
5606ef3266
commit
a91ea6c1e4
|
@ -119,14 +119,10 @@ src/Explorer/Panes/ContextualPaneBase.ts
|
||||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
src/Explorer/Panes/DeleteDatabaseConfirmationPane.test.ts
|
||||||
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts
|
||||||
src/Explorer/Panes/GraphStylingPane.ts
|
src/Explorer/Panes/GraphStylingPane.ts
|
||||||
# src/Explorer/Panes/NewVertexPane.ts
|
|
||||||
src/Explorer/Panes/PaneComponents.ts
|
src/Explorer/Panes/PaneComponents.ts
|
||||||
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
src/Explorer/Panes/RenewAdHocAccessPane.ts
|
||||||
src/Explorer/Panes/SetupNotebooksPane.ts
|
src/Explorer/Panes/SetupNotebooksPane.ts
|
||||||
src/Explorer/Panes/SwitchDirectoryPane.ts
|
src/Explorer/Panes/SwitchDirectoryPane.ts
|
||||||
src/Explorer/Panes/Tables/EditTableEntityPane.ts
|
|
||||||
src/Explorer/Panes/Tables/EntityPropertyViewModel.ts
|
|
||||||
src/Explorer/Panes/Tables/TableEntityPane.ts
|
|
||||||
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
|
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
|
||||||
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
|
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
|
||||||
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
|
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
|
||||||
|
|
|
@ -22,6 +22,4 @@ ko.components.register("throughput-input-autopilot-v3", ThroughputInputComponent
|
||||||
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
ko.components.register("add-database-pane", new PaneComponents.AddDatabasePaneComponent());
|
||||||
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
ko.components.register("add-collection-pane", new PaneComponents.AddCollectionPaneComponent());
|
||||||
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
ko.components.register("graph-styling-pane", new PaneComponents.GraphStylingPaneComponent());
|
||||||
ko.components.register("table-add-entity-pane", new PaneComponents.TableAddEntityPaneComponent());
|
|
||||||
ko.components.register("table-edit-entity-pane", new PaneComponents.TableEditEntityPaneComponent());
|
|
||||||
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
ko.components.register("cassandra-add-collection-pane", new PaneComponents.CassandraAddCollectionPaneComponent());
|
||||||
|
|
|
@ -2,8 +2,6 @@ import AddCollectionPaneTemplate from "./AddCollectionPane.html";
|
||||||
import AddDatabasePaneTemplate from "./AddDatabasePane.html";
|
import AddDatabasePaneTemplate from "./AddDatabasePane.html";
|
||||||
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
|
import CassandraAddCollectionPaneTemplate from "./CassandraAddCollectionPane.html";
|
||||||
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
import GraphStylingPaneTemplate from "./GraphStylingPane.html";
|
||||||
import TableAddEntityPaneTemplate from "./Tables/TableAddEntityPane.html";
|
|
||||||
import TableEditEntityPaneTemplate from "./Tables/TableEditEntityPane.html";
|
|
||||||
|
|
||||||
export class PaneComponent {
|
export class PaneComponent {
|
||||||
constructor(data: any) {
|
constructor(data: any) {
|
||||||
|
@ -38,23 +36,6 @@ export class GraphStylingPaneComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TableAddEntityPaneComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: PaneComponent,
|
|
||||||
template: TableAddEntityPaneTemplate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TableEditEntityPaneComponent {
|
|
||||||
constructor() {
|
|
||||||
return {
|
|
||||||
viewModel: PaneComponent,
|
|
||||||
template: TableEditEntityPaneTemplate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class CassandraAddCollectionPaneComponent {
|
export class CassandraAddCollectionPaneComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,225 +0,0 @@
|
||||||
import * as ko from "knockout";
|
|
||||||
import _ from "underscore";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import Explorer from "../../Explorer";
|
|
||||||
import * as TableConstants from "../../Tables/Constants";
|
|
||||||
import * as Entities from "../../Tables/Entities";
|
|
||||||
import { CassandraAPIDataClient, CassandraTableKey } from "../../Tables/TableDataClient";
|
|
||||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
|
||||||
import * as Utilities from "../../Tables/Utilities";
|
|
||||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
|
||||||
import TableEntityPane from "./TableEntityPane";
|
|
||||||
|
|
||||||
export default class EditTableEntityPane extends TableEntityPane {
|
|
||||||
container: Explorer;
|
|
||||||
visible: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public originEntity: Entities.ITableEntity;
|
|
||||||
public originalNumberOfProperties: number;
|
|
||||||
private originalDocument: any;
|
|
||||||
|
|
||||||
constructor(options: ViewModels.PaneOptions) {
|
|
||||||
super(options);
|
|
||||||
this.submitButtonText("Update Entity");
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
this.submitButtonText("Update Row");
|
|
||||||
}
|
|
||||||
this.scrollId = ko.observable<string>("editEntityScroll");
|
|
||||||
}
|
|
||||||
|
|
||||||
public submit() {
|
|
||||||
if (!this.canApply()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let entity: Entities.ITableEntity = this.updateEntity(this.displayedAttributes());
|
|
||||||
this.container.tableDataClient
|
|
||||||
.updateDocument(this.tableViewModel.queryTablesTab.collection, this.originalDocument, entity)
|
|
||||||
.then((newEntity: Entities.ITableEntity) => {
|
|
||||||
var numberOfProperties = 0;
|
|
||||||
for (var property in newEntity) {
|
|
||||||
if (
|
|
||||||
property !== TableEntityProcessor.keyProperties.attachments &&
|
|
||||||
property !== TableEntityProcessor.keyProperties.etag &&
|
|
||||||
property !== TableEntityProcessor.keyProperties.resourceId &&
|
|
||||||
property !== TableEntityProcessor.keyProperties.self &&
|
|
||||||
(userContext.apiType !== "Cassandra" || property !== TableConstants.EntityKeyNames.RowKey)
|
|
||||||
) {
|
|
||||||
numberOfProperties++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var propertiesDelta = numberOfProperties - this.originalNumberOfProperties;
|
|
||||||
|
|
||||||
return this.tableViewModel
|
|
||||||
.updateCachedEntity(newEntity)
|
|
||||||
.then(() => {
|
|
||||||
if (!this.tryInsertNewHeaders(this.tableViewModel, newEntity)) {
|
|
||||||
this.tableViewModel.redrawTableThrottled();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// Selecting updated entity
|
|
||||||
this.tableViewModel.selected.removeAll();
|
|
||||||
this.tableViewModel.selected.push(newEntity);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public open() {
|
|
||||||
this.displayedAttributes(this.constructDisplayedAttributes(this.originEntity));
|
|
||||||
if (userContext.apiType === "Tables") {
|
|
||||||
this.originalDocument = TableEntityProcessor.convertEntitiesToDocuments(
|
|
||||||
[<Entities.ITableEntityForTablesAPI>this.originEntity],
|
|
||||||
this.tableViewModel.queryTablesTab.collection
|
|
||||||
)[0]; // TODO change for Cassandra
|
|
||||||
this.originalDocument.id = ko.observable<string>(this.originalDocument.id);
|
|
||||||
} else {
|
|
||||||
this.originalDocument = this.originEntity;
|
|
||||||
}
|
|
||||||
this.updateIsActionEnabled();
|
|
||||||
super.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
private constructDisplayedAttributes(entity: Entities.ITableEntity): EntityPropertyViewModel[] {
|
|
||||||
var displayedAttributes: EntityPropertyViewModel[] = [];
|
|
||||||
const keys = Object.keys(entity);
|
|
||||||
keys &&
|
|
||||||
keys.forEach((key: string) => {
|
|
||||||
if (
|
|
||||||
key !== TableEntityProcessor.keyProperties.attachments &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.etag &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.self &&
|
|
||||||
(userContext.apiType !== "Cassandra" || key !== TableConstants.EntityKeyNames.RowKey)
|
|
||||||
) {
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
const cassandraKeys = this.tableViewModel.queryTablesTab.collection.cassandraKeys.partitionKeys
|
|
||||||
.concat(this.tableViewModel.queryTablesTab.collection.cassandraKeys.clusteringKeys)
|
|
||||||
.map((key) => key.property);
|
|
||||||
var entityAttribute: Entities.ITableEntityAttribute = entity[key];
|
|
||||||
var entityAttributeType: string = entityAttribute.$;
|
|
||||||
var displayValue: any = this.getPropertyDisplayValue(entity, key, entityAttributeType);
|
|
||||||
var removable: boolean = false;
|
|
||||||
// TODO figure out validation story for blob and Inet so we can allow adding/editing them
|
|
||||||
const nonEditableType: boolean =
|
|
||||||
entityAttributeType === TableConstants.CassandraType.Blob ||
|
|
||||||
entityAttributeType === TableConstants.CassandraType.Inet;
|
|
||||||
|
|
||||||
displayedAttributes.push(
|
|
||||||
new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
entityAttributeType,
|
|
||||||
displayValue,
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
false,
|
|
||||||
/* default valid name */ true,
|
|
||||||
/* default valid value */ true,
|
|
||||||
/* isRequired */ false,
|
|
||||||
removable,
|
|
||||||
/*value editable*/ !_.contains<string>(cassandraKeys, key) && !nonEditableType
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
var entityAttribute: Entities.ITableEntityAttribute = entity[key];
|
|
||||||
var entityAttributeType: string = entityAttribute.$;
|
|
||||||
var displayValue: any = this.getPropertyDisplayValue(entity, key, entityAttributeType);
|
|
||||||
var editable: boolean = this.isAttributeEditable(key, entityAttributeType);
|
|
||||||
// As per VSO:189935, Binary properties are read-only, we still want to be able to remove them.
|
|
||||||
var removable: boolean = editable || entityAttributeType === TableConstants.TableType.Binary;
|
|
||||||
|
|
||||||
displayedAttributes.push(
|
|
||||||
new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
key,
|
|
||||||
entityAttributeType,
|
|
||||||
displayValue,
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
editable,
|
|
||||||
/* default valid name */ true,
|
|
||||||
/* default valid value */ true,
|
|
||||||
/* isRequired */ false,
|
|
||||||
removable
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
(<CassandraAPIDataClient>this.container.tableDataClient)
|
|
||||||
.getTableSchema(this.tableViewModel.queryTablesTab.collection)
|
|
||||||
.then((properties: CassandraTableKey[]) => {
|
|
||||||
properties &&
|
|
||||||
properties.forEach((property) => {
|
|
||||||
if (!_.contains(keys, property.property)) {
|
|
||||||
this.insertAttribute(property.property, property.type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return displayedAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateEntity(displayedAttributes: EntityPropertyViewModel[]): Entities.ITableEntity {
|
|
||||||
var updatedEntity: any = {};
|
|
||||||
displayedAttributes &&
|
|
||||||
displayedAttributes.forEach((attribute: EntityPropertyViewModel) => {
|
|
||||||
if (attribute.name() && (userContext.apiType !== "Cassandra" || attribute.value() !== "")) {
|
|
||||||
var value = attribute.getPropertyValue();
|
|
||||||
var type = attribute.type();
|
|
||||||
if (type === TableConstants.TableType.Int64) {
|
|
||||||
value = Utilities.padLongWithZeros(value);
|
|
||||||
}
|
|
||||||
updatedEntity[attribute.name()] = {
|
|
||||||
_: value,
|
|
||||||
$: type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return updatedEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isAttributeEditable(attributeName: string, entityAttributeType: string) {
|
|
||||||
return !(
|
|
||||||
attributeName === TableConstants.EntityKeyNames.PartitionKey ||
|
|
||||||
attributeName === TableConstants.EntityKeyNames.RowKey ||
|
|
||||||
attributeName === TableConstants.EntityKeyNames.Timestamp ||
|
|
||||||
// As per VSO:189935, Making Binary properties read-only in Edit Entity dialog until we have a full story for it.
|
|
||||||
entityAttributeType === TableConstants.TableType.Binary
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPropertyDisplayValue(entity: Entities.ITableEntity, name: string, type: string): any {
|
|
||||||
var attribute: Entities.ITableEntityAttribute = entity[name];
|
|
||||||
var displayValue: any = attribute._;
|
|
||||||
var isBinary: boolean = type === TableConstants.TableType.Binary;
|
|
||||||
|
|
||||||
// Showing the value in base64 for binary properties since that is what the Azure Storage Client Library expects.
|
|
||||||
// This means that, even if the Azure Storage API returns a byte[] of binary content, it needs that same array
|
|
||||||
// *base64 - encoded * as the value for the updated property or the whole update operation will fail.
|
|
||||||
if (isBinary && displayValue && $.isArray(displayValue.data)) {
|
|
||||||
var bytes: number[] = displayValue.data;
|
|
||||||
displayValue = this.getBase64DisplayValue(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getBase64DisplayValue(bytes: number[]): string {
|
|
||||||
var displayValue: string = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
var chars: string[] = bytes.map((byte: number) => String.fromCharCode(byte));
|
|
||||||
var toEncode: string = chars.join("");
|
|
||||||
displayValue = window.btoa(toEncode);
|
|
||||||
} catch (error) {
|
|
||||||
// Error
|
|
||||||
}
|
|
||||||
|
|
||||||
return displayValue;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
import * as ko from "knockout";
|
|
||||||
|
|
||||||
import * as DateTimeUtilities from "../../Tables/QueryBuilder/DateTimeUtilities";
|
|
||||||
import * as EntityPropertyNameValidator from "./Validators/EntityPropertyNameValidator";
|
|
||||||
import EntityPropertyValueValidator from "./Validators/EntityPropertyValueValidator";
|
|
||||||
import * as Constants from "../../Tables/Constants";
|
|
||||||
import * as Utilities from "../../Tables/Utilities";
|
|
||||||
import TableEntityPane from "./TableEntityPane";
|
|
||||||
|
|
||||||
export interface IValidationResult {
|
|
||||||
isInvalid: boolean;
|
|
||||||
help: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IActionEnabledDialog {
|
|
||||||
updateIsActionEnabled: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* View model for an entity proprety
|
|
||||||
*/
|
|
||||||
export default class EntityPropertyViewModel {
|
|
||||||
/* Constants */
|
|
||||||
public static noTooltip = "";
|
|
||||||
// Maximum number of custom properties, see Azure Service Data Model
|
|
||||||
// At https://msdn.microsoft.com/library/azure/dd179338.aspx
|
|
||||||
public static maximumNumberOfProperties = 252;
|
|
||||||
|
|
||||||
// Labels
|
|
||||||
public closeButtonLabel: string = "Close"; // localize
|
|
||||||
|
|
||||||
/* Observables */
|
|
||||||
public name: ko.Observable<string>;
|
|
||||||
public type: ko.Observable<string>;
|
|
||||||
public value: ko.Observable<any>;
|
|
||||||
public inputType: ko.Computed<string>;
|
|
||||||
|
|
||||||
public nameTooltip: ko.Observable<string>;
|
|
||||||
public isInvalidName: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public valueTooltip: ko.Observable<string>;
|
|
||||||
public isInvalidValue: ko.Observable<boolean>;
|
|
||||||
|
|
||||||
public namePlaceholder: ko.Observable<string>;
|
|
||||||
public valuePlaceholder: ko.Observable<string>;
|
|
||||||
|
|
||||||
public hasFocus: ko.Observable<boolean>;
|
|
||||||
public valueHasFocus: ko.Observable<boolean>;
|
|
||||||
public isDateType: ko.Computed<boolean>;
|
|
||||||
|
|
||||||
public editable: boolean; // If a property's name or type is editable, these two are always the same regarding editability.
|
|
||||||
public valueEditable: boolean; // If a property's value is editable, could be different from name or type.
|
|
||||||
public removable: boolean; // If a property is removable, usually, PartitionKey, RowKey and TimeStamp (if applicable) are not removable.
|
|
||||||
public isRequired: boolean; // If a property's value is required, used to differentiate the place holder label.
|
|
||||||
public ignoreEmptyValue: boolean;
|
|
||||||
|
|
||||||
/* Members */
|
|
||||||
private tableEntityPane: TableEntityPane;
|
|
||||||
private _validator: EntityPropertyValueValidator;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
tableEntityPane: TableEntityPane,
|
|
||||||
name: string,
|
|
||||||
type: string,
|
|
||||||
value: any,
|
|
||||||
namePlaceholder: string = "",
|
|
||||||
valuePlaceholder: string = "",
|
|
||||||
editable: boolean = false,
|
|
||||||
defaultValidName: boolean = true,
|
|
||||||
defaultValidValue: boolean = false,
|
|
||||||
isRequired: boolean = false,
|
|
||||||
removable: boolean = editable,
|
|
||||||
valueEditable: boolean = editable,
|
|
||||||
ignoreEmptyValue: boolean = false
|
|
||||||
) {
|
|
||||||
this.name = ko.observable<string>(name);
|
|
||||||
this.type = ko.observable<string>(type);
|
|
||||||
this.isDateType = ko.pureComputed<boolean>(() => this.type() === Constants.TableType.DateTime);
|
|
||||||
if (this.isDateType()) {
|
|
||||||
value = value ? DateTimeUtilities.getLocalDateTime(value) : value;
|
|
||||||
}
|
|
||||||
this.value = ko.observable(value);
|
|
||||||
this.inputType = ko.pureComputed<string>(() => {
|
|
||||||
if (!this.valueHasFocus() && !this.value() && this.isDateType()) {
|
|
||||||
return Constants.InputType.Text;
|
|
||||||
}
|
|
||||||
return Utilities.getInputTypeFromDisplayedName(this.type());
|
|
||||||
});
|
|
||||||
|
|
||||||
this.namePlaceholder = ko.observable<string>(namePlaceholder);
|
|
||||||
this.valuePlaceholder = ko.observable<string>(valuePlaceholder);
|
|
||||||
|
|
||||||
this.editable = editable;
|
|
||||||
this.isRequired = isRequired;
|
|
||||||
this.removable = removable;
|
|
||||||
this.valueEditable = valueEditable;
|
|
||||||
|
|
||||||
this._validator = new EntityPropertyValueValidator(isRequired);
|
|
||||||
|
|
||||||
this.tableEntityPane = tableEntityPane;
|
|
||||||
|
|
||||||
this.nameTooltip = ko.observable<string>(EntityPropertyViewModel.noTooltip);
|
|
||||||
this.isInvalidName = ko.observable<boolean>(!defaultValidName);
|
|
||||||
this.name.subscribe((name: string) => this.validateName(name));
|
|
||||||
if (!defaultValidName) {
|
|
||||||
this.validateName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.valueTooltip = ko.observable<string>(EntityPropertyViewModel.noTooltip);
|
|
||||||
this.isInvalidValue = ko.observable<boolean>(!defaultValidValue);
|
|
||||||
this.value.subscribe((value: string) => this.validateValue(value, this.type()));
|
|
||||||
if (!defaultValidValue) {
|
|
||||||
this.validateValue(value, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type.subscribe((type: string) => this.validateValue(this.value(), type));
|
|
||||||
|
|
||||||
this.hasFocus = ko.observable<boolean>(false);
|
|
||||||
this.valueHasFocus = ko.observable<boolean>(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the Javascript value of the entity property based on its EDM type.
|
|
||||||
*/
|
|
||||||
public getPropertyValue(): any {
|
|
||||||
var value: string = this.value();
|
|
||||||
if (this.type() === Constants.TableType.DateTime) {
|
|
||||||
value = DateTimeUtilities.getUTCDateTime(value);
|
|
||||||
}
|
|
||||||
return this._validator.parseValue(value, this.type());
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateName(name: string): void {
|
|
||||||
var result: IValidationResult = this.isInvalidNameInput(name);
|
|
||||||
|
|
||||||
this.isInvalidName(result.isInvalid);
|
|
||||||
this.nameTooltip(result.help);
|
|
||||||
this.namePlaceholder(result.help);
|
|
||||||
this.tableEntityPane.updateIsActionEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateValue(value: string, type: string): void {
|
|
||||||
var result: IValidationResult = this.isInvalidValueInput(value, type);
|
|
||||||
if (!result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isInvalidValue(result.isInvalid);
|
|
||||||
this.valueTooltip(result.help);
|
|
||||||
this.valuePlaceholder(result.help);
|
|
||||||
this.tableEntityPane.updateIsActionEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
private isInvalidNameInput(name: string): IValidationResult {
|
|
||||||
return EntityPropertyNameValidator.validate(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private isInvalidValueInput(value: string, type: string): IValidationResult {
|
|
||||||
if (this.ignoreEmptyValue && this.value() === "") {
|
|
||||||
return { isInvalid: false, help: "" };
|
|
||||||
}
|
|
||||||
return this._validator.validate(value, type);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
<div data-bind="visible: visible">
|
|
||||||
<div
|
|
||||||
class="contextual-pane-out"
|
|
||||||
data-bind="
|
|
||||||
click: cancel,
|
|
||||||
clickBubble: false"
|
|
||||||
></div>
|
|
||||||
<div class="contextual-pane" style="width: 700px" id="addtableentitypane">
|
|
||||||
<!-- Add table entity form - Start -->
|
|
||||||
<div
|
|
||||||
class="contextual-pane-in"
|
|
||||||
data-bind="
|
|
||||||
visible: !isEditing()"
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
class="paneContentContainer"
|
|
||||||
data-bind="
|
|
||||||
submit: submit"
|
|
||||||
>
|
|
||||||
<!-- Add table entity header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
|
||||||
<div
|
|
||||||
id="closeAddEntityPane"
|
|
||||||
class="closeImg"
|
|
||||||
role="button"
|
|
||||||
aria-label="Close pane"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: cancel, event: { keydown: onCloseKeyPress }"
|
|
||||||
>
|
|
||||||
<img src="../../../../images/close-black.svg" title="Close" alt="Close" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity header - End -->
|
|
||||||
<div class="tableParamContent paneContentContainer">
|
|
||||||
<div class="entity-table">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-header" data-bind="text: attributeNameLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-type-header" data-bind="text: dataTypeLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-value-header" data-bind="text: attributeValueLabel"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-scroll-box" id="addEntityScroll">
|
|
||||||
<div class="entity-table" data-bind="foreach: displayedAttributes">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-column">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="entity-table-field entity-table-property-column"
|
|
||||||
required
|
|
||||||
data-bind="
|
|
||||||
textInput: name,
|
|
||||||
attr: { title: nameTooltip, placeholder: namePlaceholder, 'aria-label': 'property name' },
|
|
||||||
css: { 'invalid-field': isInvalidName },
|
|
||||||
readOnly: !editable,
|
|
||||||
hasFocus: hasFocus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-cell entity-table-type-column">
|
|
||||||
<select
|
|
||||||
class="entity-table-field"
|
|
||||||
data-bind="
|
|
||||||
options: $parent.edmTypes,
|
|
||||||
optionsAfterRender: $parent.setOptionDisable,
|
|
||||||
value: type,
|
|
||||||
attr: { 'aria-label': 'type' },
|
|
||||||
enable: editable,
|
|
||||||
readOnly: !editable"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
<!-- ko ifnot: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
id="addTableEntityValue"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
textInput: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType, 'aria-label': 'value' },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- ko if: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
value: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable,
|
|
||||||
hasFocus: valueHasFocus"
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<div class="entity-table-cell entity-table-action-column" data-bind="if: removable || valueEditable">
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
title="Edit property"
|
|
||||||
role="button"
|
|
||||||
aria-label="Edit property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.editAttribute.bind($data, $index()), visible: valueEditable, event: { keydown: $parent.onEditPropertyKeyDown.bind($data, $index()) }"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/Edit_entity.svg" alt="Edit" />
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
title="Delete property"
|
|
||||||
role="button"
|
|
||||||
aria-label="Delete property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.removeAttribute.bind($data, $index()), visible: removable, event: { keydown: $parent.onDeletePropertyKeyDown.bind($data, $index()) }"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/delete.svg" alt="Cancel" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table addProperty">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell">
|
|
||||||
<span
|
|
||||||
class="commandButton"
|
|
||||||
id="addProperty"
|
|
||||||
role="button"
|
|
||||||
aria-label="Add property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="visible: canAdd, click: insertAttribute, event: { keydown: onAddPropertyKeyDown }"
|
|
||||||
autofocus
|
|
||||||
>
|
|
||||||
<img class="addPropertyImg" src="/Add-property.svg" alt="Insert attribute" />
|
|
||||||
<span data-bind="text: addButtonLabel"> </span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="paneFooter">
|
|
||||||
<div class="leftpanel-okbut">
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
class="btncreatecoll1"
|
|
||||||
data-bind="value: submitButtonText, event: { keydown: onSubmitKeyPress }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity form - End -->
|
|
||||||
<!-- Add table entity editor - Start -->
|
|
||||||
<div id="editor-panel-addEntity" data-bind="visible: isEditing()" style="display: none">
|
|
||||||
<div data-bind="with: editingProperty()">
|
|
||||||
<!-- Add table entity editor header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span
|
|
||||||
class="backBtn"
|
|
||||||
aria-label="Back"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: $parent.finishEditingAttribute, event: { keydown: $parent.onBackButtonKeyDown }"
|
|
||||||
>
|
|
||||||
<img src="/RevertBack.svg" alt="BackIcon" />
|
|
||||||
</span>
|
|
||||||
<span class="edit-value-text" data-bind="text: name"></span>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity editor header - End -->
|
|
||||||
<div class="seconddivbg paddingspan2">
|
|
||||||
<textarea
|
|
||||||
class="entity-editor-expanded"
|
|
||||||
id="textAreaEditProperty"
|
|
||||||
tabindex="0"
|
|
||||||
rows="21"
|
|
||||||
data-bind="value: value, attr: { 'aria-label': name }"
|
|
||||||
style="width: 95%"
|
|
||||||
autofocus
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Add table entity editor - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,187 +0,0 @@
|
||||||
<div data-bind="visible: visible">
|
|
||||||
<div
|
|
||||||
class="contextual-pane-out"
|
|
||||||
data-bind="
|
|
||||||
click: cancel,
|
|
||||||
clickBubble: false"
|
|
||||||
></div>
|
|
||||||
<div class="contextual-pane" style="width: 700px" id="edittableentitypane">
|
|
||||||
<!-- Edit table entity form - Start -->
|
|
||||||
<div
|
|
||||||
class="contextual-pane-in"
|
|
||||||
data-bind="
|
|
||||||
visible: !isEditing()"
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
class="paneContentContainer"
|
|
||||||
data-bind="
|
|
||||||
submit: submit"
|
|
||||||
>
|
|
||||||
<!-- Edit table entity header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
|
||||||
<div
|
|
||||||
class="closeImg"
|
|
||||||
role="button"
|
|
||||||
aria-label="Close pane"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: cancel, event: { keydown: onCloseKeyPress }"
|
|
||||||
>
|
|
||||||
<img src="../../../../images/close-black.svg" title="Close" alt="Close" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity header - End -->
|
|
||||||
<div class="tableParamContent paneContentContainer">
|
|
||||||
<div class="entity-table">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-header" data-bind="text: attributeNameLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-type-header" data-bind="text: dataTypeLabel"></div>
|
|
||||||
<div class="entity-table-cell entity-table-value-header" data-bind="text: attributeValueLabel"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-scroll-box" id="editEntityScroll">
|
|
||||||
<div class="entity-table" data-bind="foreach: displayedAttributes">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell entity-table-property-column">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="entity-table-field entity-table-property-column"
|
|
||||||
required
|
|
||||||
data-bind="
|
|
||||||
textInput: name,
|
|
||||||
attr: { title: nameTooltip, placeholder: namePlaceholder, 'aria-label': 'property name' },
|
|
||||||
css: { 'invalid-field': isInvalidName },
|
|
||||||
readOnly: !editable,
|
|
||||||
hasFocus: hasFocus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table-cell entity-table-type-column">
|
|
||||||
<select
|
|
||||||
class="entity-table-field"
|
|
||||||
data-bind="
|
|
||||||
options: $parent.edmTypes,
|
|
||||||
optionsAfterRender: $parent.setOptionDisable,
|
|
||||||
value: type,
|
|
||||||
attr: { 'aria-label': 'type' },
|
|
||||||
enable: editable,
|
|
||||||
readOnly: !editable"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
<!-- ko ifnot: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
textInput: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType, 'aria-label': 'value' },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<!-- ko if: isDateType -->
|
|
||||||
<div class="entity-table-cell entity-table-value-column">
|
|
||||||
<input
|
|
||||||
class="entity-table-field"
|
|
||||||
step="1"
|
|
||||||
data-bind="
|
|
||||||
value: value,
|
|
||||||
attr: { title: valueTooltip, placeholder: valuePlaceholder, type: inputType, 'aria-label': 'value' },
|
|
||||||
css: { 'invalid-field': isInvalidValue },
|
|
||||||
readOnly: !valueEditable,
|
|
||||||
hasFocus: valueHasFocus"
|
|
||||||
autocomplete="off"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- /ko -->
|
|
||||||
<div class="entity-table-cell entity-table-action-column" data-bind="if: removable || valueEditable">
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
role="button"
|
|
||||||
aria-label="Edit property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.editAttribute.bind($data, $index()), visible: valueEditable, event: { keydown: $parent.onEditPropertyKeyDown.bind($data, $index()) }"
|
|
||||||
title="Edit property"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/Edit_entity.svg" alt="Edit attribute" />
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="entity-Edit-Cancel"
|
|
||||||
role="button"
|
|
||||||
aria-label="Delete property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="click: $parent.removeAttribute.bind($data, $index()), visible: removable, event: { keydown: $parent.onDeletePropertyKeyDown.bind($data, $index()) }"
|
|
||||||
title="Delete property"
|
|
||||||
>
|
|
||||||
<img class="entity-Editor-Cancel-Img" src="/delete.svg" alt="Remove attribute" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="entity-table addProperty">
|
|
||||||
<div class="entity-table-row">
|
|
||||||
<div class="entity-table-cell">
|
|
||||||
<span
|
|
||||||
class="commandButton"
|
|
||||||
role="button"
|
|
||||||
aria-label="Add property"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="visible: canAdd, click: insertAttribute, event: { keydown: onAddPropertyKeyDown }"
|
|
||||||
autofocus
|
|
||||||
>
|
|
||||||
<img class="addPropertyImg" src="/Add-property.svg" alt="Add attribute" />
|
|
||||||
<span data-bind="text: addButtonLabel"> </span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="paneFooter">
|
|
||||||
<div class="leftpanel-okbut">
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
value="Update Entity"
|
|
||||||
class="btncreatecoll1"
|
|
||||||
data-bind="value: submitButtonText, event: { keydown: onSubmitKeyPress }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity form - End -->
|
|
||||||
<!-- Edit table entity editor - Start -->
|
|
||||||
<div id="editor-panel-editEntity" data-bind="visible: isEditing()" style="display: none">
|
|
||||||
<div data-bind="with: editingProperty()">
|
|
||||||
<!-- Edit table entity editor header - Start -->
|
|
||||||
<div class="firstdivbg headerline">
|
|
||||||
<span
|
|
||||||
class="backBtn"
|
|
||||||
aria-label="Back"
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
data-bind="
|
|
||||||
click: $parent.finishEditingAttribute, event: { keydown: $parent.onBackButtonKeyDown }"
|
|
||||||
>
|
|
||||||
<img src="/RevertBack.svg" alt="BackIcon" />
|
|
||||||
</span>
|
|
||||||
<span class="edit-value-text" data-bind="text: name"></span>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity editor header - End -->
|
|
||||||
<div class="seconddivbg paddingspan2">
|
|
||||||
<textarea
|
|
||||||
class="entity-editor-expanded"
|
|
||||||
id="editor-area"
|
|
||||||
tabindex="0"
|
|
||||||
rows="21"
|
|
||||||
data-bind="value: value, attr: { 'aria-label': name }"
|
|
||||||
autofocus
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Edit table entity editor - End -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,279 +0,0 @@
|
||||||
import * as ko from "knockout";
|
|
||||||
import _ from "underscore";
|
|
||||||
import { KeyCodes } from "../../../Common/Constants";
|
|
||||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
|
||||||
import { userContext } from "../../../UserContext";
|
|
||||||
import * as TableConstants from "../../Tables/Constants";
|
|
||||||
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
|
|
||||||
import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel";
|
|
||||||
import * as Entities from "../../Tables/Entities";
|
|
||||||
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
|
|
||||||
import * as Utilities from "../../Tables/Utilities";
|
|
||||||
import { ContextualPaneBase } from "../ContextualPaneBase";
|
|
||||||
import EntityPropertyViewModel from "./EntityPropertyViewModel";
|
|
||||||
|
|
||||||
// Class with variables and functions that are common to both adding and editing entities
|
|
||||||
export default abstract class TableEntityPane extends ContextualPaneBase {
|
|
||||||
protected static requiredFieldsForTablesAPI: string[] = [
|
|
||||||
TableConstants.EntityKeyNames.PartitionKey,
|
|
||||||
TableConstants.EntityKeyNames.RowKey,
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Labels */
|
|
||||||
public attributeNameLabel = "Property Name"; // localize
|
|
||||||
public dataTypeLabel = "Type"; // localize
|
|
||||||
public attributeValueLabel = "Value"; // localize
|
|
||||||
|
|
||||||
/* Controls */
|
|
||||||
public removeButtonLabel = "Remove"; // localize
|
|
||||||
public editButtonLabel = "Edit"; // localize
|
|
||||||
public addButtonLabel = "Add Property"; // localize
|
|
||||||
|
|
||||||
public edmTypes: ko.ObservableArray<string> = ko.observableArray([
|
|
||||||
TableConstants.TableType.String,
|
|
||||||
TableConstants.TableType.Boolean,
|
|
||||||
TableConstants.TableType.Binary,
|
|
||||||
TableConstants.TableType.DateTime,
|
|
||||||
TableConstants.TableType.Double,
|
|
||||||
TableConstants.TableType.Guid,
|
|
||||||
TableConstants.TableType.Int32,
|
|
||||||
TableConstants.TableType.Int64,
|
|
||||||
]);
|
|
||||||
|
|
||||||
public canAdd: ko.Computed<boolean>;
|
|
||||||
public canApply: ko.Observable<boolean>;
|
|
||||||
public displayedAttributes = ko.observableArray<EntityPropertyViewModel>();
|
|
||||||
public editingProperty = ko.observable<EntityPropertyViewModel>();
|
|
||||||
public isEditing = ko.observable<boolean>(false);
|
|
||||||
public submitButtonText = ko.observable<string>();
|
|
||||||
|
|
||||||
public tableViewModel: TableEntityListViewModel;
|
|
||||||
|
|
||||||
protected scrollId: ko.Observable<string>;
|
|
||||||
|
|
||||||
constructor(options: ViewModels.PaneOptions) {
|
|
||||||
super(options);
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
this.edmTypes([
|
|
||||||
TableConstants.CassandraType.Text,
|
|
||||||
TableConstants.CassandraType.Ascii,
|
|
||||||
TableConstants.CassandraType.Bigint,
|
|
||||||
TableConstants.CassandraType.Blob,
|
|
||||||
TableConstants.CassandraType.Boolean,
|
|
||||||
TableConstants.CassandraType.Decimal,
|
|
||||||
TableConstants.CassandraType.Double,
|
|
||||||
TableConstants.CassandraType.Float,
|
|
||||||
TableConstants.CassandraType.Int,
|
|
||||||
TableConstants.CassandraType.Uuid,
|
|
||||||
TableConstants.CassandraType.Varchar,
|
|
||||||
TableConstants.CassandraType.Varint,
|
|
||||||
TableConstants.CassandraType.Inet,
|
|
||||||
TableConstants.CassandraType.Smallint,
|
|
||||||
TableConstants.CassandraType.Tinyint,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.canAdd = ko.computed<boolean>(() => {
|
|
||||||
// Cassandra can't add since the schema can't be changed once created
|
|
||||||
if (userContext.apiType === "Cassandra") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Adding '2' to the maximum to take into account PartitionKey and RowKey
|
|
||||||
return this.displayedAttributes().length < EntityPropertyViewModel.maximumNumberOfProperties + 2;
|
|
||||||
});
|
|
||||||
this.canApply = ko.observable<boolean>(true);
|
|
||||||
this.editingProperty(this.displayedAttributes()[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeAttribute = (index: number, data: any): void => {
|
|
||||||
this.displayedAttributes.splice(index, 1);
|
|
||||||
this.updateIsActionEnabled();
|
|
||||||
document.getElementById("addProperty").focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
public editAttribute = (index: number, data: EntityPropertyViewModel): void => {
|
|
||||||
this.editingProperty(data);
|
|
||||||
this.isEditing(true);
|
|
||||||
document.getElementById("textAreaEditProperty").focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
public finishEditingAttribute = (): void => {
|
|
||||||
this.isEditing(false);
|
|
||||||
this.editingProperty(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
public onKeyUp = (data: any, event: KeyboardEvent): boolean => {
|
|
||||||
var handled: boolean = Utilities.onEsc(event, ($sourceElement: JQuery) => {
|
|
||||||
this.finishEditingAttribute();
|
|
||||||
});
|
|
||||||
|
|
||||||
return !handled;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onAddPropertyKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.insertAttribute();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onEditPropertyKeyDown = (
|
|
||||||
index: number,
|
|
||||||
data: EntityPropertyViewModel,
|
|
||||||
event: KeyboardEvent,
|
|
||||||
source: any
|
|
||||||
): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.editAttribute(index, data);
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onDeletePropertyKeyDown = (
|
|
||||||
index: number,
|
|
||||||
data: EntityPropertyViewModel,
|
|
||||||
event: KeyboardEvent,
|
|
||||||
source: any
|
|
||||||
): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.removeAttribute(index, data);
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public onBackButtonKeyDown = (source: any, event: KeyboardEvent): boolean => {
|
|
||||||
if (event.keyCode === KeyCodes.Enter || event.keyCode === KeyCodes.Space) {
|
|
||||||
this.finishEditingAttribute();
|
|
||||||
event.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
public insertAttribute = (name?: string, type?: string): void => {
|
|
||||||
let entityProperty: EntityPropertyViewModel;
|
|
||||||
if (!!name && !!type && userContext.apiType === "Cassandra") {
|
|
||||||
// TODO figure out validation story for blob and Inet so we can allow adding/editing them
|
|
||||||
const nonEditableType: boolean =
|
|
||||||
type === TableConstants.CassandraType.Blob || type === TableConstants.CassandraType.Inet;
|
|
||||||
entityProperty = new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
"", // default to empty string
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
/* editable */ false,
|
|
||||||
/* default valid name */ false,
|
|
||||||
/* default valid value */ true,
|
|
||||||
/* isRequired */ false,
|
|
||||||
/* removable */ false,
|
|
||||||
/*value editable*/ !nonEditableType
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
entityProperty = new EntityPropertyViewModel(
|
|
||||||
this,
|
|
||||||
"",
|
|
||||||
this.edmTypes()[0], // default to the first Edm type: 'string'
|
|
||||||
"", // default to empty string
|
|
||||||
/* namePlaceholder */ undefined,
|
|
||||||
/* valuePlaceholder */ undefined,
|
|
||||||
/* editable */ true,
|
|
||||||
/* default valid name */ false,
|
|
||||||
/* default valid value */ true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.displayedAttributes.push(entityProperty);
|
|
||||||
this.updateIsActionEnabled();
|
|
||||||
this.scrollToBottom();
|
|
||||||
|
|
||||||
entityProperty.hasFocus(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
public updateIsActionEnabled(needRequiredFields: boolean = true): void {
|
|
||||||
var properties: EntityPropertyViewModel[] = this.displayedAttributes() || [];
|
|
||||||
var disable: boolean = _.some(properties, (property: EntityPropertyViewModel) => {
|
|
||||||
return property.isInvalidName() || property.isInvalidValue();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canApply(!disable);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected entityFromAttributes(displayedAttributes: EntityPropertyViewModel[]): Entities.ITableEntity {
|
|
||||||
var entity: any = {};
|
|
||||||
|
|
||||||
displayedAttributes &&
|
|
||||||
displayedAttributes.forEach((attribute: EntityPropertyViewModel) => {
|
|
||||||
if (attribute.name() && (attribute.value() !== "" || attribute.isRequired)) {
|
|
||||||
var value = attribute.getPropertyValue();
|
|
||||||
var type = attribute.type();
|
|
||||||
if (type === TableConstants.TableType.Int64) {
|
|
||||||
value = Utilities.padLongWithZeros(value);
|
|
||||||
}
|
|
||||||
entity[attribute.name()] = {
|
|
||||||
_: value,
|
|
||||||
$: type,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing Binary from Add Entity dialog until we have a full story for it.
|
|
||||||
protected setOptionDisable(option: Node, value: string): void {
|
|
||||||
ko.applyBindingsToNode(option, { disable: value === TableConstants.TableType.Binary }, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse the updated entity to see if there are any new attributes that old headers don't have.
|
|
||||||
* In this case, add these attributes names as new headers.
|
|
||||||
* Remarks: adding new headers will automatically trigger table redraw.
|
|
||||||
*/
|
|
||||||
protected tryInsertNewHeaders(viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean {
|
|
||||||
var newHeaders: string[] = [];
|
|
||||||
const keys = Object.keys(newEntity);
|
|
||||||
keys &&
|
|
||||||
keys.forEach((key: string) => {
|
|
||||||
if (
|
|
||||||
!_.contains(viewModel.headers, key) &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.attachments &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.etag &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.resourceId &&
|
|
||||||
key !== TableEntityProcessor.keyProperties.self &&
|
|
||||||
(userContext.apiType !== "Cassandra" || key !== TableConstants.EntityKeyNames.RowKey)
|
|
||||||
) {
|
|
||||||
newHeaders.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var newHeadersInserted: boolean = false;
|
|
||||||
if (newHeaders.length) {
|
|
||||||
if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) {
|
|
||||||
newHeaders = viewModel.headers.concat(newHeaders);
|
|
||||||
}
|
|
||||||
viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false);
|
|
||||||
newHeadersInserted = true;
|
|
||||||
}
|
|
||||||
return newHeadersInserted;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected scrollToBottom(): void {
|
|
||||||
var scrollBox = document.getElementById(this.scrollId());
|
|
||||||
var isScrolledToBottom = scrollBox.scrollHeight - scrollBox.clientHeight <= scrollBox.scrollHeight + 1;
|
|
||||||
if (isScrolledToBottom) {
|
|
||||||
scrollBox.scrollTop = scrollBox.scrollHeight - scrollBox.clientHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue