import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app import { CompileScript, ScriptOptions } from "../../util/Scripting"; import { FieldView, FieldViewProps } from './FieldView'; import "./KeyValueBox.scss"; import { KeyValuePair } from "./KeyValuePair"; import React = require("react"); import { NumCast, Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Doc, Field, FieldResult } from "../../../new_fields/Doc"; import { ComputedField } from "../../../new_fields/ScriptField"; import { SetupDrag } from "../../util/DragManager"; import { Docs } from "../../documents/Documents"; import { RawDataOperationParameters } from "../../northstar/model/idea/idea"; import { Templates } from "../Templates"; import { List } from "../../../new_fields/List"; import { TextField } from "../../util/ProsemirrorCopy/prompt"; import { RichTextField } from "../../../new_fields/RichTextField"; import { ImageField } from "../../../new_fields/URLField"; @observer export class KeyValueBox extends React.Component { private _mainCont = React.createRef(); private _keyHeader = React.createRef(); @observable private rows: KeyValuePair[] = []; public static LayoutString(fieldStr: string = "data") { return FieldView.LayoutString(KeyValueBox, fieldStr); } @observable private _keyInput: string = ""; @observable private _valueInput: string = ""; @computed get splitPercentage() { return NumCast(this.props.Document.schemaSplitPercentage, 50); } get fieldDocToLayout() { return this.props.fieldKey ? FieldValue(Cast(this.props.Document[this.props.fieldKey], Doc)) : this.props.Document; } constructor(props: FieldViewProps) { super(props); } @action onEnterKey = (e: React.KeyboardEvent): void => { if (e.key === 'Enter') { if (this._keyInput && this._valueInput && this.fieldDocToLayout) { if (KeyValueBox.SetField(this.fieldDocToLayout, this._keyInput, this._valueInput)) { this._keyInput = ""; this._valueInput = ""; } } } } public static SetField(doc: Doc, key: string, value: string) { let eq = value.startsWith("="); let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; let dubEq = value.startsWith(":="); value = dubEq ? value.substr(2) : value; let options: ScriptOptions = { addReturn: true, params: { this: "Doc" } }; if (dubEq) options.typecheck = false; let script = CompileScript(value, options); if (!script.compiled) { return false; } let field = new ComputedField(script); if (!dubEq) { let res = script.run({ this: target }); if (!res.success) return false; field = res.result; } if (Field.IsField(field, true)) { target[key] = field; return true; } return false; } onPointerDown = (e: React.PointerEvent): void => { if (e.buttons === 1 && this.props.isSelected()) { e.stopPropagation(); } } onPointerWheel = (e: React.WheelEvent): void => { e.stopPropagation(); } createTable = () => { let doc = this.fieldDocToLayout; if (!doc) { return Loading...; } let realDoc = doc; let ids: { [key: string]: string } = {}; let protos = Doc.GetAllPrototypes(doc); for (const proto of protos) { Object.keys(proto).forEach(key => { if (!(key in ids)) { ids[key] = key; } }); } let rows: JSX.Element[] = []; let i = 0; for (let key of Object.keys(ids).sort()) { rows.push( { if (el) this.rows.push(el); }} keyWidth={100 - this.splitPercentage} rowStyle={"keyValueBox-" + (i++ % 2 ? "oddRow" : "evenRow")} key={key} keyName={key} />); } return rows; } @action keyChanged = (e: React.ChangeEvent) => { this._keyInput = e.currentTarget.value; } @action valueChanged = (e: React.ChangeEvent) => { this._valueInput = e.currentTarget.value; } newKeyValue = () => ( ) @action onDividerMove = (e: PointerEvent): void => { let nativeWidth = this._mainCont.current!.getBoundingClientRect(); this.props.Document.schemaSplitPercentage = Math.max(0, 100 - Math.round((e.clientX - nativeWidth.left) / nativeWidth.width * 100)); } @action onDividerUp = (e: PointerEvent): void => { document.removeEventListener("pointermove", this.onDividerMove); document.removeEventListener('pointerup', this.onDividerUp); } onDividerDown = (e: React.PointerEvent) => { e.stopPropagation(); e.preventDefault(); document.addEventListener("pointermove", this.onDividerMove); document.addEventListener('pointerup', this.onDividerUp); } getTemplate = async () => { let parent = Docs.FreeformDocument([], { width: 800, height: 800, title: "Template" }); for (let row of this.rows.filter(row => row.isChecked)) { await this.createTemplateField(parent, row); row.uncheck(); } return parent; } createTemplateField = async (parent: Doc, row: KeyValuePair) => { let collectionKeyProp = `fieldKey={"data"}`; let metaKey = row.props.keyName; let metaKeyProp = `fieldKey={"${metaKey}"}`; let sourceDoc = await Cast(this.props.Document.data, Doc); if (!sourceDoc) { return; } let target = this.inferType(sourceDoc[metaKey], metaKey); let template = Doc.MakeAlias(target); template.proto = parent; template.title = metaKey; template.nativeWidth = 300; template.nativeHeight = 300; template.embed = true; template.isTemplate = true; template.templates = new List([Templates.TitleBar(metaKey)]); if (target.backgroundLayout) { let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`; let collectionAnoKeyProp = `fieldKey={"annotations"}`; template.layout = StrCast(target.layout).replace(collectionAnoKeyProp, metaAnoKeyProp); template.backgroundLayout = StrCast(target.backgroundLayout).replace(collectionKeyProp, metaKeyProp); } else { template.layout = StrCast(target.layout).replace(collectionKeyProp, metaKeyProp); } Doc.AddDocToList(parent, "data", template); row.uncheck(); } inferType = (field: FieldResult, metaKey: string) => { let options = { width: 300, height: 300, title: metaKey }; if (field instanceof RichTextField || typeof field === "string" || typeof field === "number") { return Docs.TextDocument(options); } else if (field instanceof List) { return Docs.FreeformDocument([], options); } else if (field instanceof ImageField) { return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options); } return new Doc; } render() { let dividerDragger = this.splitPercentage === 0 ? (null) :
; return (
{this.createTable()} {this.newKeyValue()}
Key Fields
{dividerDragger}
); } }