From 04887c8a578147015421d3909bd100c82ac5e31d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Mon, 8 Jul 2019 19:55:15 -0400 Subject: fixed readonly and hopefully cursor proto bugs --- src/client/views/nodes/KeyValueBox.tsx | 81 +++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 26 deletions(-) (limited to 'src/client/views/nodes/KeyValueBox.tsx') diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 9407d742c..fbabe224e 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -18,6 +18,7 @@ import { List } from "../../../new_fields/List"; import { TextField } from "../../util/ProsemirrorCopy/prompt"; import { RichTextField } from "../../../new_fields/RichTextField"; import { ImageField } from "../../../new_fields/URLField"; +import { SelectionManager } from "../../util/SelectionManager"; @observer export class KeyValueBox extends React.Component { @@ -167,44 +168,72 @@ export class KeyValueBox extends React.Component { return parent; } - createTemplateField = async (parent: Doc, row: KeyValuePair) => { - let collectionKeyProp = `fieldKey={"data"}`; - let metaKey = row.props.keyName; - let metaKeyProp = `fieldKey={"${metaKey}"}`; + createTemplateField = async (parentStackingDoc: 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 = 0; + // template.nativeHeight = 0; + // 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); + // } + + let metaKey = row.props.keyName; 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 = 0; - template.nativeHeight = 0; - 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); + let fieldTemplate = this.inferType(sourceDoc[metaKey], metaKey); + + // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) + let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); + let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); + if (backgroundLayout) { + layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`); + backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); } - Doc.AddDocToList(parent, "data", template); + let nw = NumCast(fieldTemplate.nativeWidth); + let nh = NumCast(fieldTemplate.nativeHeight); + + fieldTemplate.title = metaKey; + fieldTemplate.layout = layout; + fieldTemplate.backgroundLayout = backgroundLayout; + fieldTemplate.nativeWidth = nw; + fieldTemplate.nativeHeight = nh; + fieldTemplate.embed = true; + fieldTemplate.isTemplate = true; + fieldTemplate.templates = new List([Templates.TitleBar(metaKey)]); + fieldTemplate.proto = Doc.GetProto(parentStackingDoc); + + Doc.AddDocToList(parentStackingDoc, "data", fieldTemplate); row.uncheck(); } - inferType = (field: FieldResult, metaKey: string) => { + inferType = (data: FieldResult, metaKey: string) => { let options = { width: 300, height: 300, title: metaKey }; - if (field instanceof RichTextField || typeof field === "string" || typeof field === "number") { + if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") { return Docs.TextDocument(options); - } else if (field instanceof List) { + } else if (data instanceof List) { return Docs.StackingDocument([], options); - } else if (field instanceof ImageField) { + } else if (data instanceof ImageField) { return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options); } return new Doc; -- cgit v1.2.3-70-g09d2 From 7fdb33cafaa3e8593d648ddba994356a9625ff56 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 9 Jul 2019 13:52:35 -0400 Subject: string list to list of text doc map in scraper and cleaned up kv pane templating interaction code --- src/client/views/nodes/DocumentContentsView.tsx | 7 +-- src/client/views/nodes/KeyValueBox.tsx | 20 ++++++-- src/scraping/buxton/scraper.py | 61 +++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes/KeyValueBox.tsx') diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index b4c0e844f..eb786d537 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -69,7 +69,7 @@ export class DocumentContentsView extends React.Component 7) return (null); if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null); return >>>>>> b49fdb1c42b9758e006521e0f404634ba396a2ac jsx={this.finalLayout} showWarnings={true} onError={(test: any) => { console.log(test); }} diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index e27ab1589..e8619584e 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -161,7 +161,7 @@ export class KeyValueBox extends React.Component { getTemplate = async () => { let parent = Docs.StackingDocument([], { width: 800, height: 800, title: "Template" }); parent.singleColumn = false; - parent.columnWidth = 50; + parent.columnWidth = 100; for (let row of this.rows.filter(row => row.isChecked)) { await this.createTemplateField(parent, row); row.uncheck(); @@ -175,7 +175,7 @@ export class KeyValueBox extends React.Component { if (!sourceDoc) { return; } - let fieldTemplate = this.inferType(sourceDoc[metaKey], metaKey); + let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey); // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); @@ -200,12 +200,24 @@ export class KeyValueBox extends React.Component { Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate); } - inferType = (data: FieldResult, metaKey: string) => { + inferType = async (data: FieldResult, metaKey: string) => { let options = { width: 300, height: 300, title: metaKey }; if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") { return Docs.TextDocument(options); } else if (data instanceof List) { - return Docs.StackingDocument([], options); + if (data.length === 0) { + return Docs.StackingDocument([], options); + } + let first = await Cast(data[0], Doc); + if (!first) { + return Docs.StackingDocument([], options); + } + switch (first.type) { + case "image": + return Docs.StackingDocument([], options); + case "text": + return Docs.TreeDocument([], options); + } } else if (data instanceof ImageField) { return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options); } diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 02c6d8b74..700269727 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -26,7 +26,7 @@ def extract_links(fileName): item = rels[rel] if item.reltype == RT.HYPERLINK and ".aspx" not in item._target: links.append(item._target) - return listify(links) + return text_doc_map(links) def extract_value(kv_string): @@ -60,6 +60,12 @@ def protofy(fieldId): } +def text_doc_map(string_list): + def guid_map(caption): + return write_text_doc(caption) + return listify(proxify_guids(list(map(guid_map, string_list)))) + + def write_schema(parse_results, display_fields, storage_key): view_guids = parse_results["child_guids"] @@ -110,6 +116,54 @@ def write_schema(parse_results, display_fields, storage_key): return view_doc_guid +def write_text_doc(content): + data_doc_guid = guid() + view_doc_guid = guid() + + view_doc = { + "_id": view_doc_guid, + "fields": { + "proto": protofy(data_doc_guid), + "x": 10, + "y": 10, + "width": 400, + "zIndex": 2, + "libraryBrush": False + }, + "__type": "Doc" + } + + data_doc = { + "_id": data_doc_guid, + "fields": { + "proto": protofy("textProto"), + "data": { + "Data": '{"doc":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"' + content + '"}]}]},"selection":{"type":"text","anchor":1,"head":1}' + '}', + "__type": "RichTextField" + }, + "title": content, + "nativeWidth": 200, + "author": "Bill Buxton", + "creationDate": { + "date": datetime.datetime.utcnow().microsecond, + "__type": "date" + }, + "isPrototype": True, + "autoHeight": True, + "page": -1, + "nativeHeight": 200, + "height": 200, + "data_text": content + }, + "__type": "Doc" + } + + db.newDocuments.insert_one(view_doc) + db.newDocuments.insert_one(data_doc) + + return view_doc_guid + + def write_image(folder, name): path = f"http://localhost:1050/files/{folder}/{name}" @@ -253,7 +307,7 @@ def parse_document(file_name: str): while lines[cur] != "Image": link_descriptions.append(lines[cur].strip()) cur += 1 - result["link_descriptions"] = listify(link_descriptions) + result["link_descriptions"] = text_doc_map(link_descriptions) result["hyperlinks"] = extract_links(source + "/" + file_name) @@ -265,7 +319,8 @@ def parse_document(file_name: str): captions.append(lines[cur + 1]) cur += 2 result["images"] = listify(images) - result["captions"] = listify(captions) + + result["captions"] = text_doc_map(captions) notes = [] if (cur < len(lines) and lines[cur] == "NOTES:"): -- cgit v1.2.3-70-g09d2 From 9d9c16939bb296a9deb38bc34cbea9870aee4f76 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 9 Jul 2019 14:33:56 -0400 Subject: collection view fix --- src/client/views/nodes/KeyValueBox.tsx | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/client/views/nodes/KeyValueBox.tsx') diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index e8619584e..b5c47f138 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -175,7 +175,9 @@ export class KeyValueBox extends React.Component { if (!sourceDoc) { return; } + let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey); + let previousViewType = fieldTemplate.viewType; // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); @@ -196,6 +198,7 @@ export class KeyValueBox extends React.Component { fieldTemplate.isTemplate = true; fieldTemplate.templates = new List([Templates.TitleBar(metaKey)]); fieldTemplate.proto = Doc.GetProto(parentStackingDoc); + previousViewType && (fieldTemplate.viewType = previousViewType); Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate); } @@ -214,8 +217,10 @@ export class KeyValueBox extends React.Component { } switch (first.type) { case "image": + console.log("STACKING VIEW CREATED for ", data); return Docs.StackingDocument([], options); case "text": + console.log("TREE VIEW CREATED for ", data); return Docs.TreeDocument([], options); } } else if (data instanceof ImageField) { -- cgit v1.2.3-70-g09d2 From 7a33f0b56f1182c19cc7ea1b276da3a674438957 Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Tue, 9 Jul 2019 14:39:23 -0400 Subject: cleanup --- src/client/views/nodes/KeyValueBox.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src/client/views/nodes/KeyValueBox.tsx') diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index b5c47f138..2f5a0f963 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -217,14 +217,12 @@ export class KeyValueBox extends React.Component { } switch (first.type) { case "image": - console.log("STACKING VIEW CREATED for ", data); return Docs.StackingDocument([], options); case "text": - console.log("TREE VIEW CREATED for ", data); return Docs.TreeDocument([], options); } } else if (data instanceof ImageField) { - return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options); + return Docs.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options); } return new Doc; } -- cgit v1.2.3-70-g09d2 From b1b69f8a4f9e34f0c8e667ec95f9fe16ebc8b2e4 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 10 Jul 2019 21:32:31 -0400 Subject: factored template creation code into Doc.ts --- src/client/views/DocumentDecorations.tsx | 28 +------------------- .../views/collections/CollectionTreeView.tsx | 1 - src/client/views/nodes/KeyValueBox.tsx | 21 +-------------- src/new_fields/Doc.ts | 30 +++++++++++++++++++++- 4 files changed, 31 insertions(+), 49 deletions(-) (limited to 'src/client/views/nodes/KeyValueBox.tsx') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 07ecc6b0f..95335020f 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -84,33 +84,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let fieldTemplate = fieldTemplateView.props.Document; let docTemplate = fieldTemplateView.props.ContainingCollectionView!.props.Document; let metaKey = text.slice(1, text.length); - - // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) - let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); - let fieldLayoutDoc = fieldTemplate; - if (fieldTemplate.layout instanceof Doc) { - fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout); - } - let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); - if (backgroundLayout) { - layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`); - backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); - } - let nw = Cast(fieldTemplate.nativeWidth, "number"); - let nh = Cast(fieldTemplate.nativeHeight, "number"); - - let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate; - layoutDelegate.layout = layout; - - fieldTemplate.title = metaKey; - fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; - fieldTemplate.backgroundLayout = backgroundLayout; - fieldTemplate.nativeWidth = nw; - fieldTemplate.nativeHeight = nh; - fieldTemplate.embed = true; - fieldTemplate.isTemplate = true; - fieldTemplate.showTitle = "title"; - fieldTemplate.proto = Doc.GetProto(docTemplate); + Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(docTemplate)); } else { if (SelectionManager.SelectedDocuments().length > 0) { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index d90c78f85..3674a743a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -73,7 +73,6 @@ class TreeView extends React.Component { @observable _collapsed: boolean = true; @computed get fieldKey() { - trace(); let keys = Array.from(Object.keys(this.resolvedDataDoc)); // bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set if (this.resolvedDataDoc.proto instanceof Doc) { let arr = Array.from(Object.keys(this.resolvedDataDoc.proto!));// bcz: Argh -- make untracked to avoid this rerunning whenever 'libraryBrush' is set diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index 2f5a0f963..467cb2b6b 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -178,26 +178,7 @@ export class KeyValueBox extends React.Component { let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey); let previousViewType = fieldTemplate.viewType; - - // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) - let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); - let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); - if (backgroundLayout) { - layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`); - backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); - } - let nw = NumCast(fieldTemplate.nativeWidth); - let nh = NumCast(fieldTemplate.nativeHeight); - - fieldTemplate.title = metaKey; - fieldTemplate.layout = layout; - fieldTemplate.backgroundLayout = backgroundLayout; - fieldTemplate.nativeWidth = nw; - fieldTemplate.nativeHeight = nh; - fieldTemplate.embed = true; - fieldTemplate.isTemplate = true; - fieldTemplate.templates = new List([Templates.TitleBar(metaKey)]); - fieldTemplate.proto = Doc.GetProto(parentStackingDoc); + Doc.MakeTemplate(fieldTemplate, metaKey, Doc.GetProto(parentStackingDoc)); previousViewType && (fieldTemplate.viewType = previousViewType); Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate); diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index c63c9ef93..3d46243e0 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -3,7 +3,7 @@ import { serializable, primitive, map, alias, list } from "serializr"; import { autoObject, SerializationHelper, Deserializable } from "../client/util/SerializationHelper"; import { DocServer } from "../client/DocServer"; import { setter, getter, getField, updateFunction, deleteProperty, makeEditable, makeReadOnly } from "./util"; -import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast } from "./Types"; +import { Cast, ToConstructor, PromiseValue, FieldValue, NumCast, BoolCast, StrCast } from "./Types"; import { listSpec } from "./Schema"; import { ObjectField } from "./ObjectField"; import { RefField, FieldId } from "./RefField"; @@ -362,4 +362,32 @@ export namespace Doc { delegate.proto = doc; return delegate; } + + export function MakeTemplate(fieldTemplate: Doc, metaKey: string, proto: Doc) { + // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??) + let backgroundLayout = StrCast(fieldTemplate.backgroundLayout); + let fieldLayoutDoc = fieldTemplate; + if (fieldTemplate.layout instanceof Doc) { + fieldLayoutDoc = Doc.MakeDelegate(fieldTemplate.layout); + } + let layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); + if (backgroundLayout) { + layout = StrCast(fieldLayoutDoc.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`); + backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`); + } + let nw = Cast(fieldTemplate.nativeWidth, "number"); + let nh = Cast(fieldTemplate.nativeHeight, "number"); + + let layoutDelegate = fieldTemplate.layout instanceof Doc ? fieldLayoutDoc : fieldTemplate; + layoutDelegate.layout = layout; + + fieldTemplate.title = metaKey; + fieldTemplate.layout = layoutDelegate !== fieldTemplate ? layoutDelegate : layout; + fieldTemplate.backgroundLayout = backgroundLayout; + fieldTemplate.nativeWidth = nw; + fieldTemplate.nativeHeight = nh; + fieldTemplate.isTemplate = true; + fieldTemplate.showTitle = "title"; + fieldTemplate.proto = proto; + } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 60ceef0a3a8c11d85434a154e542424d34d9562c Mon Sep 17 00:00:00 2001 From: Tyler Schicke Date: Sat, 13 Jul 2019 16:46:49 -0400 Subject: Refactored style in DocDecs and added metadata entry to DocDecs --- src/client/util/Scripting.ts | 3 +- src/client/views/DocumentDecorations.scss | 44 ++++++++++-------- src/client/views/DocumentDecorations.tsx | 31 +++++++++---- src/client/views/MetadataEntryMenu.scss | 10 +++++ src/client/views/MetadataEntryMenu.tsx | 74 +++++++++++++++++++++++++++++++ src/client/views/nodes/KeyValueBox.tsx | 31 ++++++++++--- src/client/views/nodes/KeyValuePair.tsx | 8 +--- src/new_fields/Doc.ts | 9 ++++ 8 files changed, 169 insertions(+), 41 deletions(-) create mode 100644 src/client/views/MetadataEntryMenu.scss create mode 100644 src/client/views/MetadataEntryMenu.tsx (limited to 'src/client/views/nodes/KeyValueBox.tsx') diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 3156c4f43..62c2cfe85 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -33,6 +33,8 @@ export interface CompileError { errors: any[]; } +export type CompileResult = CompiledScript | CompileError; + export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; @@ -61,7 +63,6 @@ export function scriptingGlobal(constructor: { new(...args: any[]): any }) { const scriptingGlobals: { [name: string]: any } = {}; -export type CompileResult = CompiledScript | CompileError; function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult { const errors = diagnostics.some(diag => diag.category === ts.DiagnosticCategory.Error); if ((options.typecheck !== false && errors) || !script) { diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index 2d430512b..1afc5c147 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -1,6 +1,7 @@ @import "globalCssVariables"; $linkGap : 3px; + .documentDecorations { position: absolute; } @@ -52,15 +53,16 @@ $linkGap : 3px; grid-column-start: 5; grid-column-end: 7; } - - #documentDecorations-borderRadius{ + + #documentDecorations-borderRadius { grid-column-start: 5; grid-column-end: 7; border-radius: 100%; - .borderRadiusTooltip{ - width:10px; - height:10px; - position:absolute; + + .borderRadiusTooltip { + width: 10px; + height: 10px; + position: absolute; } } @@ -68,8 +70,9 @@ $linkGap : 3px; #documentDecorations-bottomRightResizer { cursor: nwse-resize; } + #documentDecorations-bottomRightResizer { - grid-row:4; + grid-row: 4; } #documentDecorations-topRightResizer, @@ -86,7 +89,8 @@ $linkGap : 3px; #documentDecorations-rightResizer { cursor: ew-resize; } - .title{ + + .title { background: $alt-accent; grid-column-start: 3; grid-column-end: 4; @@ -129,7 +133,6 @@ $linkGap : 3px; .linkFlyout { grid-column: 2/4; - margin-top: $linkGap; } .linkButton-empty:hover { @@ -145,6 +148,7 @@ $linkGap : 3px; } .link-button-container { + margin-top: $linkGap; grid-column: 1/4; width: auto; height: auto; @@ -152,9 +156,12 @@ $linkGap : 3px; flex-direction: row; } +.linkButtonWrapper { + pointer-events: auto; + padding-right: 5px; +} + .linkButton-linker { - margin-left: 5px; - margin-top: $linkGap; height: 20px; width: 20px; text-align: center; @@ -169,7 +176,8 @@ $linkGap : 3px; transform: scale(1.05); } -.linkButton-empty, .linkButton-nonempty { +.linkButton-empty, +.linkButton-nonempty { height: 20px; width: 20px; border-radius: 50%; @@ -194,9 +202,6 @@ $linkGap : 3px; } .templating-menu { - position: absolute; - bottom: 0; - left: 50px; pointer-events: auto; text-transform: uppercase; letter-spacing: 2px; @@ -208,15 +213,17 @@ $linkGap : 3px; align-items: center; } -.fa-icon-link { +.documentdecorations-icon { margin-top: 3px; } -.templating-button { + +.templating-button, +.docDecs-tagButton { width: 20px; height: 20px; border-radius: 50%; opacity: 0.9; - font-size:14; + font-size: 14; background-color: $dark-color; color: $light-color; text-align: center; @@ -238,6 +245,7 @@ $linkGap : 3px; background-color: $light-color-secondary; padding: 2px 12px; list-style: none; + .templateToggle { text-align: left; } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index c24d66ee4..56fbd75a0 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,5 +1,5 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faLink } from '@fortawesome/free-solid-svg-icons'; +import { faLink, faTag } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { action, computed, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; @@ -27,11 +27,13 @@ import React = require("react"); import { RichTextField } from '../../new_fields/RichTextField'; import { LinkManager } from '../util/LinkManager'; import { ObjectField } from '../../new_fields/ObjectField'; +import { MetadataEntryMenu } from './MetadataEntryMenu'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; library.add(faLink); +library.add(faTag); @observer export class DocumentDecorations extends React.Component<{}, { value: string }> { @@ -596,8 +598,8 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> if (!canEmbed) return (null); return (
-
- +
+
); @@ -610,10 +612,9 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> this._textDoc = thisDoc; return (
-
+
{/* */} - T -
+
); @@ -634,6 +635,17 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> } } + get metadataMenu() { + return ( +
+ SelectionManager.SelectedDocuments().map(dv => dv.props.Document)} />}>{/* tfs: @bcz This might need to be the data document? */} +
+
+
+ ); + } + render() { var bounds = this.Bounds; let seldoc = SelectionManager.SelectedDocuments().length ? SelectionManager.SelectedDocuments()[0] : undefined; @@ -723,10 +735,13 @@ export class DocumentDecorations extends React.Component<{}, { value: string }>
- +
- +
+ +
+ {this.metadataMenu} {this.considerEmbed()} {/* {this.considerTooltip()} */} diff --git a/src/client/views/MetadataEntryMenu.scss b/src/client/views/MetadataEntryMenu.scss new file mode 100644 index 000000000..73e5b6a73 --- /dev/null +++ b/src/client/views/MetadataEntryMenu.scss @@ -0,0 +1,10 @@ +.metadataEntry-input { + width: 40%; + margin-left: 5px; + margin-right: 5px; +} + +.metadataEntry-outerDiv { + display: flex; + width: 300px; +} \ No newline at end of file diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx new file mode 100644 index 000000000..0dc7e0220 --- /dev/null +++ b/src/client/views/MetadataEntryMenu.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import "./MetadataEntryMenu.scss"; +import { observer } from 'mobx-react'; +import { observable, action } from 'mobx'; +import { KeyValueBox } from './nodes/KeyValueBox'; +import { Doc } from '../../new_fields/Doc'; + +export type DocLike = Doc | Doc[] | Promise | Promise; +export interface MetadataEntryProps { + docs: DocLike | (() => DocLike); + onError?: () => boolean; +} + +@observer +export class MetadataEntryMenu extends React.Component{ + @observable private _currentKey: string = ""; + @observable private _currentValue: string = ""; + + @action + onKeyChange = (e: React.ChangeEvent) => { + this._currentKey = e.target.value; + } + + @action + onValueChange = (e: React.ChangeEvent) => { + this._currentValue = e.target.value; + } + + onValueKeyDown = async (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + const script = KeyValueBox.CompileKVPScript(this._currentValue); + if (!script) return; + let doc = this.props.docs; + if (typeof doc === "function") { + doc = doc(); + } + doc = await doc; + let success: boolean; + if (doc instanceof Doc) { + success = KeyValueBox.ApplyKVPScript(doc, this._currentKey, script); + } else { + success = doc.every(d => KeyValueBox.ApplyKVPScript(d, this._currentKey, script)); + } + if (!success) { + if (this.props.onError) { + if (this.props.onError()) { + this.clearInputs(); + } + } else { + this.clearInputs(); + } + } else { + this.clearInputs(); + } + } + } + + @action + clearInputs = () => { + this._currentKey = ""; + this._currentValue = ""; + } + + render() { + return ( +
+ Key: + + Value: + +
+ ); + } +} \ No newline at end of file diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx index eb8abf2c7..c9dd9a64e 100644 --- a/src/client/views/nodes/KeyValueBox.tsx +++ b/src/client/views/nodes/KeyValueBox.tsx @@ -2,7 +2,7 @@ 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 { CompileScript, ScriptOptions, CompiledScript } from "../../util/Scripting"; import { FieldView, FieldViewProps } from './FieldView'; import "./KeyValueBox.scss"; import { KeyValuePair } from "./KeyValuePair"; @@ -21,6 +21,12 @@ import { ImageField } from "../../../new_fields/URLField"; import { SelectionManager } from "../../util/SelectionManager"; import { listSpec } from "../../../new_fields/Schema"; +export type KVPScript = { + script: CompiledScript; + type: "computed" | "script" | false; + onDelegate: boolean; +}; + @observer export class KeyValueBox extends React.Component { private _mainCont = React.createRef(); @@ -48,22 +54,27 @@ export class KeyValueBox extends React.Component { } } } - public static SetField(doc: Doc, key: string, value: string) { + public static CompileKVPScript(value: string): KVPScript | undefined { let eq = value.startsWith("="); - let target = eq ? doc : Doc.GetProto(doc); value = eq ? value.substr(1) : value; - let dubEq = value.startsWith(":=") ? 1 : value.startsWith(";=") ? 2 : 0; + const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false; 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; + return undefined; } + return { script, type: dubEq, onDelegate: eq }; + } + + public static ApplyKVPScript(doc: Doc, key: string, kvpScript: KVPScript): boolean { + const { script, type, onDelegate } = kvpScript; + const target = onDelegate ? doc : Doc.GetProto(doc); let field: Field; - if (dubEq === 1) { + if (type === "computed") { field = new ComputedField(script); - } else if (dubEq === 2) { + } else if (type === "script") { field = new ScriptField(script); } else { let res = script.run({ this: target }); @@ -77,6 +88,12 @@ export class KeyValueBox extends React.Component { return false; } + public static SetField(doc: Doc, key: string, value: string) { + const script = this.CompileKVPScript(value); + if (!script) return false; + return this.ApplyKVPScript(doc, key, script); + } + onPointerDown = (e: React.PointerEvent): void => { if (e.buttons === 1 && this.props.isSelected()) { e.stopPropagation(); diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index b5db52ac7..209782242 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -92,13 +92,7 @@ export class KeyValuePair extends React.Component { contents={contents} height={36} GetValue={() => { - const onDelegate = Object.keys(props.Document).includes(props.fieldKey); - - let field = FieldValue(props.Document[props.fieldKey]); - if (Field.IsField(field)) { - return (onDelegate ? "=" : "") + Field.toScriptString(field); - } - return ""; + return Field.toKeyValueString(props.Document, props.fieldKey); }} SetValue={(value: string) => KeyValueBox.SetField(props.Document, props.fieldKey, value)}> diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 0e5f8b4d9..c5f9e7adf 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -12,6 +12,15 @@ import { scriptingGlobal } from "../client/util/Scripting"; import { List } from "./List"; export namespace Field { + export function toKeyValueString(doc: Doc, key: string): string { + const onDelegate = Object.keys(doc).includes(key); + + let field = FieldValue(doc[key]); + if (Field.IsField(field)) { + return (onDelegate ? "=" : "") + Field.toScriptString(field); + } + return ""; + } export function toScriptString(field: Field): string { if (typeof field === "string") { return `"${field}"`; -- cgit v1.2.3-70-g09d2