From 0d263dba422a96c5d22f29a9b6411ff074c92645 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 25 Jan 2020 17:28:57 -0500 Subject: added carousel view --- .../views/collections/CollectionCarouselView.tsx | 62 ++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/client/views/collections/CollectionCarouselView.tsx (limited to 'src/client/views/collections/CollectionCarouselView.tsx') diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx new file mode 100644 index 000000000..f2cc2479f --- /dev/null +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -0,0 +1,62 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { observable } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { documentSchema } from '../../../new_fields/documentSchemas'; +import { makeInterface } from '../../../new_fields/Schema'; +import { NumCast } from '../../../new_fields/Types'; +import { DragManager } from '../../util/DragManager'; +import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; +import "./CollectionCarouselView.scss"; +import { CollectionSubView } from './CollectionSubView'; +import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons'; +import { Doc } from '../../../new_fields/Doc'; + + + + +type CarouselDocument = makeInterface<[typeof documentSchema,]>; +const CarouselDocument = makeInterface(documentSchema); + +@observer +export class CollectionCarouselView extends CollectionSubView(CarouselDocument) { + @observable public addMenuToggle = React.createRef(); + private _dropDisposer?: DragManager.DragDropDisposer; + + componentWillUnmount() { + this._dropDisposer && this._dropDisposer(); + } + + componentDidMount() { + } + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + this._dropDisposer && this._dropDisposer(); + if (ele) { + this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this)); + } + } + + render() { + const index = NumCast(this.layoutDoc._itemIndex); + return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) : +
+ +
this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length}> + +
+
this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length}> + +
+
; + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 5e67aa8ab95e40730acb584cb5ff812fe470608f Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Mon, 27 Jan 2020 17:32:47 -0500 Subject: template fixes --- src/client/util/DropConverter.ts | 8 +- src/client/util/RichTextRules.ts | 62 +++++++------- src/client/util/RichTextSchema.tsx | 97 +++++++++++++--------- src/client/views/DocComponent.tsx | 4 +- .../views/collections/CollectionCarouselView.tsx | 4 +- src/client/views/nodes/FieldView.tsx | 6 +- src/client/views/nodes/FormattedTextBox.tsx | 2 +- 7 files changed, 104 insertions(+), 79 deletions(-) (limited to 'src/client/views/collections/CollectionCarouselView.tsx') diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 9c068d2d7..8d92de28f 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -4,7 +4,9 @@ import { DocumentType } from "../documents/DocumentTypes"; import { ObjectField } from "../../new_fields/ObjectField"; import { StrCast } from "../../new_fields/Types"; import { Docs } from "../documents/Documents"; -import { ScriptField } from "../../new_fields/ScriptField"; +import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; +import { RichTextField } from "../../new_fields/RichTextField"; +import { Compute } from "google-auth-library"; export function makeTemplate(doc: Doc): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; @@ -18,6 +20,10 @@ export function makeTemplate(doc: Doc): boolean { Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)); } else if (d.type === DocumentType.COL) { any = makeTemplate(d) || any; + } else if (d.data instanceof RichTextField) { + d._textTemplate = ComputedField.MakeFunction("copyField(this.data)", { this: Doc.name }); + d.isTemplateForField = "data"; + any = true; } }); return any; diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index bc8a0abb1..0de0e21fd 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -2,14 +2,15 @@ import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from import { schema } from "./RichTextSchema"; import { wrappingInputRule } from "./prosemirrorPatches"; import { NodeSelection, TextSelection } from "prosemirror-state"; -import { StrCast, Cast } from "../../new_fields/Types"; -import { Doc } from "../../new_fields/Doc"; +import { StrCast, Cast, NumCast } from "../../new_fields/Types"; +import { Doc, DataSym } from "../../new_fields/Doc"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { Docs, DocUtils } from "../documents/Documents"; import { Id } from "../../new_fields/FieldSymbols"; import { DocServer } from "../DocServer"; import { returnFalse, Utils } from "../../Utils"; import RichTextMenu from "./RichTextMenu"; +import { RichTextField } from "../../new_fields/RichTextField"; export const inpRules = { rules: [ @@ -70,36 +71,26 @@ export const inpRules = { return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size })); }), - // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode) + // create a text display of a metadata field new InputRule( - new RegExp(/!$/), + new RegExp(/\[\[([a-zA-Z_ \-0-9]+)\]\]$/), (state, match, start, end) => { - if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null; - const value = state.doc.textBetween(start, end); - - const node = (state.doc.resolve(start) as any).nodeAfter; - const sm = state.storedMarks || undefined; - const fieldView = state.schema.nodes.dashField.create({ fieldKey: StrCast(value) }); - const replaced = node ? state.tr.replaceRangeWith(start, end, fieldView).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; - return replaced.doc.nodeSize > end - 2 ? replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))) : replaced; + const fieldKey = match[1]; + const fieldView = state.schema.nodes.dashField.create({ fieldKey: StrCast(fieldKey) }); + return state.tr.deleteRange(start, end).insert(start, fieldView); }), - // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode) + // create a hyperlink portal new InputRule( - new RegExp(/@$/), + new RegExp(/\[\[:([a-zA-Z_ \-0-9]+)\]\]$/), (state, match, start, end) => { - if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null; - - const value = state.doc.textBetween(start, end); - if (value) { - DocServer.GetRefField(value).then(docx => { - const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, _width: 500, _height: 500, }, value); - DocUtils.Publish(target, value, returnFalse, returnFalse); - DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", ""); - }); - const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value, targetId: value }); - return state.tr.addMark(start, end, link); - } - return state.tr; + const docId = match[1].substring(1); + DocServer.GetRefField(docId).then(docx => { + const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, }, docId); + DocUtils.Publish(target, docId, returnFalse, returnFalse); + DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", ""); + }); + const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId }); + return state.tr.addMark(start, end, link); }), // stop using active style new InputRule( @@ -209,10 +200,21 @@ export const inpRules = { new InputRule( new RegExp(/%#$/), (state, match, start, end) => { - const target = Docs.Create.TextDocument("", { _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + const textDoc = Doc.GetProto(Cast((schema as any).Document[DataSym], Doc, null)!); + const numInlines = NumCast(textDoc.inlineTextCount); + textDoc.inlineTextCount = numInlines + 1; + const inlineFieldKey = "inline" + numInlines; + const textDocInline = Docs.Create.TextDocument("", { _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + textDocInline.layoutKey = "layout_" + inlineFieldKey; + textDocInline.customTitle = true; + textDocInline.title = "inline"; + textDocInline.isTemplateForField = inlineFieldKey; + textDocInline.proto = textDoc; + textDoc[textDocInline.layoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); + textDoc[inlineFieldKey] = "-inline-"; const node = (state.doc.resolve(start) as any).nodeAfter; - const newNode = schema.nodes.dashComment.create({ docid: target[Id] }); - const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" }); + const newNode = schema.nodes.dashComment.create({ docid: textDocInline[Id] }); + const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: textDocInline[Id], float: "right" }); const sm = state.storedMarks || undefined; const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 21916e3d6..0deedbe39 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -18,6 +18,7 @@ import { Transform } from "./Transform"; import React = require("react"); import { BoolCast, NumCast, StrCast } from "../../new_fields/Types"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; +import { ObjectField } from "../../new_fields/ObjectField"; const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; @@ -682,7 +683,7 @@ export class DashDocCommentView { if (target) { const expand = target.hidden; const tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true }); - view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs + view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs setTimeout(() => { expand && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc)); try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))); } catch (e) { } @@ -734,12 +735,6 @@ export class DashDocView { this._dashSpan.style.height = node.attrs.height; this._dashSpan.style.position = "absolute"; this._dashSpan.style.display = "inline-block"; - const removeDoc = () => { - const pos = getPos(); - const ns = new NodeSelection(view.state.doc.resolve(pos)); - view.dispatch(view.state.tr.setSelection(ns).deleteSelection()); - return true; - }; this._dashSpan.onpointerleave = () => { const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid); if (ele) { @@ -752,46 +747,24 @@ export class DashDocView { (ele as HTMLDivElement).style.backgroundColor = "orange"; } }; + const removeDoc = () => { + const pos = getPos(); + const ns = new NodeSelection(view.state.doc.resolve(pos)); + view.dispatch(view.state.tr.setSelection(ns).deleteSelection()); + return true; + }; DocServer.GetRefField(node.attrs.docid).then(async dashDoc => { if (dashDoc instanceof Doc) { self._dashDoc = dashDoc; dashDoc.hideSidebar = true; - if (node.attrs.width !== dashDoc.width + "px" || node.attrs.height !== dashDoc.height + "px") { + if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") { try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made - view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc.width + "px", height: dashDoc.height + "px" })); + view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" })); } catch (e) { console.log(e); } } - this._reactionDisposer && this._reactionDisposer(); - this._reactionDisposer = reaction(() => dashDoc[HeightSym]() + dashDoc[WidthSym](), () => { - this._dashSpan.style.height = this._outer.style.height = dashDoc[HeightSym]() + "px"; - this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px"; - }); - ReactDOM.render(, this._dashSpan); + self.doRender(dashDoc, removeDoc); } }); const self = this; @@ -807,6 +780,49 @@ export class DashDocView { this._outer.appendChild(this._dashSpan); (this as any).dom = this._outer; } + doRender(dashDoc: Doc, removeDoc: any) { + const self = this; + const finalLayout = Doc.expandTemplateLayout(dashDoc, this._textBox.dataDoc); + if (!finalLayout) setTimeout(() => self.doRender(dashDoc, removeDoc), 0); + else { + const layoutKey = StrCast(finalLayout.layoutKey); + const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1]; + if (finalLayout !== dashDoc && finalKey) { + const finalLayoutField = finalLayout[finalKey] + finalLayoutField instanceof ObjectField && (finalLayout._textTemplate = ObjectField.MakeCopy(finalLayoutField)); + } + this._reactionDisposer && this._reactionDisposer(); + this._reactionDisposer = reaction(() => [finalLayout[WidthSym](), finalLayout[HeightSym]()], (dim) => { + this._dashSpan.style.width = this._outer.style.width = dim[0] + "px"; + this._dashSpan.style.height = this._outer.style.height = dim[1] + "px"; + }, { fireImmediately: true }); + ReactDOM.render(, this._dashSpan); + } + } destroy() { this._reactionDisposer && this._reactionDisposer(); } @@ -837,8 +853,9 @@ export class DashFieldView { this._labelSpan.style.fontWeight = "bold"; this._labelSpan.style.fontSize = "larger"; this._labelSpan.innerHTML = `${node.attrs.fieldKey}: `; + const ddoc = tbox.props.DataDoc || tbox.dataDoc; this._reactionDisposer && this._reactionDisposer(); - this._reactionDisposer = reaction(() => this._textBoxDoc[DataSym][node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true }); + this._reactionDisposer = reaction(() => ddoc[node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true }); this._fieldWrapper.appendChild(this._labelSpan); this._fieldWrapper.appendChild(this._fieldSpan); (this as any).dom = this._fieldWrapper; @@ -1012,7 +1029,7 @@ export class SummaryView { view.dispatch(view.state.tr. setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed) replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it - setNodeMarkup(getPos(), undefined, attrs)); // update the attrs + setNodeMarkup(getPos(), undefined, attrs)); // update the attrs e.preventDefault(); e.stopPropagation(); this._collapsed.className = this.className(visible); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index d98301cf6..a31997a10 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -34,7 +34,7 @@ export function DocExtendableComponent

(schemaCt //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed get Document(): T { return schemaCtor(this.props.Document); } @computed get layoutDoc() { return Doc.Layout(this.props.Document); } - @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } + @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Cast(this.props.Document.resolvedDataDoc, Doc, null) || Doc.GetProto(this.props.Document)) as Doc; } active = (outsideReaction?: boolean) => !this.props.Document.isBackground && (this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.renderDepth === 0);// && !InkingControl.Instance.selectedTool; // bcz: inking state shouldn't affect static tools } return Component; @@ -57,7 +57,7 @@ export function DocAnnotatableComponent

(schema //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @computed get Document(): T { return schemaCtor(this.props.Document); } @computed get layoutDoc() { return Doc.Layout(this.props.Document); } - @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Doc.GetProto(this.props.Document)) as Doc; } + @computed get dataDoc() { return (this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : Cast(this.props.Document.resolvedDataDoc, Doc, null) || Doc.GetProto(this.props.Document)) as Doc; } _annotationKey: string = "annotations"; public set annotationKey(val: string) { this._annotationKey = val; } diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index f2cc2479f..9f32bb0c9 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -45,13 +45,13 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) Document={this.childLayoutPairs[index].layout} DataDocument={this.childLayoutPairs[index].data} getTransform={this.props.ScreenToLocalTransform} /> -

this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length}> +
{ e.stopPropagation(); this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length }}>
-
this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length}> +
{ e.stopPropagation(); this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length }}> { // if (typeof field === "string") { // return

{field}

; // } - else if (field instanceof RichTextField) { - return ; - } + // else if (field instanceof RichTextField) { + // return ; + // } else if (field instanceof ImageField) { return ; } diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index fab267358..5c9f1a754 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -496,7 +496,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & this._reactionDisposer = reaction( () => { - const field = this.dataDoc ? Cast(this.dataDoc[this.props.fieldKey], RichTextField) : undefined; + const field = Cast(this.props.Document._textTemplate || this.dataDoc[this.props.fieldKey], RichTextField); return field ? field.Data : RichTextUtils.Initialize(); }, incomingValue => { -- cgit v1.2.3-70-g09d2 From d1ed73e0a0fa3f3da9811edfe3233c663d34cffa Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 28 Jan 2020 15:15:47 -0500 Subject: functional child templates for Buxton view --- .../views/collections/CollectionCarouselView.tsx | 52 ++++++++++------- .../views/collections/CollectionPivotView.tsx | 66 +++++++--------------- .../CollectionStackingViewFieldColumn.tsx | 1 + .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 + src/new_fields/Doc.ts | 4 ++ 5 files changed, 58 insertions(+), 66 deletions(-) (limited to 'src/client/views/collections/CollectionCarouselView.tsx') diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 9f32bb0c9..815dfb35a 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { observable } from 'mobx'; +import { observable, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { documentSchema } from '../../../new_fields/documentSchemas'; @@ -36,27 +36,37 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) } } - render() { + advance = (e: React.MouseEvent) => { + e.stopPropagation(); + this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) + 1) % this.childLayoutPairs.length; + } + goback = (e: React.MouseEvent) => { + e.stopPropagation(); + this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; + } + + @computed get content() { const index = NumCast(this.layoutDoc._itemIndex); return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) : -
- -
{ e.stopPropagation(); this.layoutDoc._itemIndex = (index - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length }}> - -
-
{ e.stopPropagation(); this.layoutDoc._itemIndex = (index + 1) % this.childLayoutPairs.length }}> - -
-
; + + } + @computed get buttons() { + return <> +
+ +
+
+ +
+ ; + } + render() { + return
+ {this.content} + {this.buttons} +
; } } \ No newline at end of file diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx index 98fc54b3b..66c7c9745 100644 --- a/src/client/views/collections/CollectionPivotView.tsx +++ b/src/client/views/collections/CollectionPivotView.tsx @@ -1,61 +1,37 @@ -import { CollectionSubView } from "./CollectionSubView"; -import React = require("react"); -import { computed, action, IReactionDisposer, reaction, runInAction, observable } from "mobx"; -import { faEdit, faChevronCircleUp } from "@fortawesome/free-solid-svg-icons"; -import { Doc, DocListCast, Field, DocCastAsync } from "../../../new_fields/Doc"; -import "./CollectionPivotView.scss"; -import { observer } from "mobx-react"; -import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; -import { CollectionTreeView } from "./CollectionTreeView"; -import { Cast, StrCast, NumCast } from "../../../new_fields/Types"; -import { Docs } from "../../documents/Documents"; -import { ScriptField, ComputedField } from "../../../new_fields/ScriptField"; -import { CompileScript, Scripting } from "../../util/Scripting"; -import { anchorPoints, Flyout } from "../TemplateMenu"; +import { faEdit } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { List } from "../../../new_fields/List"; +import { action, computed, IReactionDisposer, observable } from "mobx"; +import { observer } from "mobx-react"; import { Set } from "typescript-collections"; -import { PrefetchProxy } from "../../../new_fields/Proxy"; -import { EditableView } from "../EditableView"; +import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; +import { ComputedField, ScriptField } from "../../../new_fields/ScriptField"; +import { Cast, StrCast } from "../../../new_fields/Types"; +import { Docs } from "../../documents/Documents"; +import { CompileScript } from "../../util/Scripting"; +import { EditableView } from "../EditableView"; +import { anchorPoints, Flyout } from "../TemplateMenu"; +import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView"; +import "./CollectionPivotView.scss"; +import { CollectionSubView } from "./CollectionSubView"; +import { CollectionTreeView } from "./CollectionTreeView"; +import React = require("react"); @observer export class CollectionPivotView extends CollectionSubView(doc => doc) { - private _narrativeDisposer: IReactionDisposer | undefined; - componentWillUnmount() { - this._narrativeDisposer?.(); - } componentDidMount() { this.props.Document._freeformLayoutEngine = "pivot"; + const childDetailed = this.props.Document.childDetailed; // bcz: needs to be here to make sure the childDetailed layout template has been loaded when the first item is clicked; if (!this.props.Document._facetCollection) { const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true }); facetCollection.target = this.props.Document; - this.props.Document.excludeFields = new List(["_facetCollection", "_docFilter", "viewSpecScript"]); + this.props.Document.excludeFields = new List(["_facetCollection", "_docFilter"]); const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; - const script = CompileScript(scriptText, { - params: { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name }, - typecheck: false, - editable: true, - }); - if (script.compiled) { - facetCollection.onCheckedClick = new ScriptField(script); - } - const openDocText = "const alias = getAlias(this); Doc.ApplyTemplateTo(childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; - this._narrativeDisposer = reaction(() => DocCastAsync(this.props.Document.childDetailed), - (childDetailedPromise) => childDetailedPromise.then(childDetailed => { - if (childDetailed) { - const openDocScript = CompileScript(openDocText, { - params: { this: Doc.name, heading: "boolean", containingTreeView: Doc.name }, - capturedVariables: { childDetailed: new PrefetchProxy(childDetailed) }, - typecheck: false, - editable: true, - }); - if (openDocScript.compiled) { - this.props.Document.onChildClick = new ScriptField(openDocScript); - } - } - }), { fireImmediately: true }); + const childText = "const alias = getAlias(this); Doc.ApplyTemplateTo(containingCollection.childDetailed, alias, 'layout_detailed'); useRightSplit(alias); "; + facetCollection.onCheckedClick = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "boolean", checked: "boolean", containingTreeView: Doc.name }); + this.props.Document.onChildClick = ScriptField.MakeScript(childText, { this: Doc.name, heading: "boolean", containingCollection: Doc.name }); this.props.Document._facetCollection = facetCollection; this.props.Document._fitToBox = true; } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 6a6f7aa6d..9cd9c1465 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -283,6 +283,7 @@ export class CollectionStackingViewFieldColumn extends React.Component this.props.parent.props.addDocument(Docs.Create.FreeformDocument([], { _width: 200, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" }); + layoutItems.push({ description: ":carousel", event: () => this.props.parent.props.addDocument(Docs.Create.CarouselDocument([], { _width: 400, _height: 200, _LODdisable: true })), icon: "compress-arrows-alt" }); layoutItems.push({ description: ":columns", event: () => this.props.parent.props.addDocument(Docs.Create.MulticolumnDocument([], { _width: 200, _height: 200 })), icon: "compress-arrows-alt" }); layoutItems.push({ description: ":image", event: () => this.props.parent.props.addDocument(Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { _width: 200, _height: 200 })), icon: "compress-arrows-alt" }); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index ea3c505d8..846d402ab 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -114,6 +114,7 @@ export class CollectionFreeFormDocumentView extends DocComponent : d instanceof Doc && MakeMetadataFieldTemplate(d, templateDoc)); (Doc.GetProto(templateField)[metadataFieldKey] = ObjectField.MakeCopy(templateField.data)); } + if (templateField.data instanceof RichTextField && templateField.data.Data) { + templateField._textTemplate = ObjectField.MakeCopy(templateField.data); + } // get the layout string that the template uses to specify its layout const templateFieldLayoutString = StrCast(Doc.LayoutField(Doc.Layout(templateField))); -- cgit v1.2.3-70-g09d2 From 0361c04360362c31e4bdd5d27b52707b4e288662 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 28 Jan 2020 18:14:59 -0500 Subject: added captions to carousel. cleaned up some stuff. --- src/client/documents/Documents.ts | 7 +++- src/client/util/RichTextSchema.tsx | 2 +- .../views/collections/CollectionCarouselView.scss | 12 ++++++ .../views/collections/CollectionCarouselView.tsx | 24 ++++++++---- .../views/collections/CollectionDockingView.tsx | 44 +++++++++++++++++++++- .../views/collections/CollectionPivotView.tsx | 5 +-- src/client/views/collections/CollectionSubView.tsx | 6 +-- .../views/collections/CollectionTreeView.tsx | 44 ++++++++++++---------- src/client/views/nodes/FormattedTextBox.tsx | 4 +- src/scraping/buxton/scraper.py | 20 ++++------ 10 files changed, 116 insertions(+), 52 deletions(-) (limited to 'src/client/views/collections/CollectionCarouselView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index a7f939d7d..e481b1a4a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -74,7 +74,8 @@ export interface DocumentOptions { _xMargin?: number; // gap between left edge of document and start of masonry/stacking layouts _yMargin?: number; // gap between top edge of dcoument and start of masonry/stacking layouts _textTemplate?: RichTextField; // template used by a formattedTextBox to create a text box to render - _itemIndex?: number; + _itemIndex?: number; // which item index the carousel viewer is showing + _hideSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts x?: number; y?: number; z?: number; @@ -127,6 +128,7 @@ export interface DocumentOptions { color?: string; treeViewHideTitle?: boolean; // whether to hide the title of a tree view treeViewOpen?: boolean; // whether this document is expanded in a tree view + treeViewChecked?: ScriptField; // script to call when a tree view checkbox is checked isFacetFilter?: boolean; // whether document functions as a facet filter in a tree view limitHeight?: number; // maximum height for newly created (eg, from pasting) text documents // [key: string]: Opt; @@ -336,7 +338,8 @@ export namespace Docs { */ export namespace Create { - const delegateKeys = ["x", "y", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "_dropAction", "_annotationOn", "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex"]; + const delegateKeys = ["x", "y", "_width", "_height", "_panX", "_panY", "_viewType", "_nativeWidth", "_nativeHeight", "_dropAction", "_annotationOn", + "_chromeStatus", "_forceActive", "_autoHeight", "_fitWidth", "_LODdisable", "_itemIndex", "_hideSidebar"]; /** * This function receives the relevant document prototype and uses diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 7051ba1c3..f667b86af 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -757,7 +757,7 @@ export class DashDocView { DocServer.GetRefField(node.attrs.docid).then(async dashDoc => { if (dashDoc instanceof Doc) { self._dashDoc = dashDoc; - dashDoc.hideSidebar = true; + dashDoc._hideSidebar = true; if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") { try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" })); diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss index 2e0e293b8..4815f1a59 100644 --- a/src/client/views/collections/CollectionCarouselView.scss +++ b/src/client/views/collections/CollectionCarouselView.scss @@ -1,6 +1,18 @@ .collectionCarouselView-outer { background: gray; + .collectionCarouselView-caption { + margin-left: 10%; + margin-right: 10%; + height: 50; + display: inline-block; + width: 80%; + } + .collectionCarouselView-image { + height: calc(100% - 50px); + display: inline-block; + width: 100%; + } } .carouselView-back { position: absolute; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index 815dfb35a..0933d5924 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -4,13 +4,14 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { documentSchema } from '../../../new_fields/documentSchemas'; import { makeInterface } from '../../../new_fields/Schema'; -import { NumCast } from '../../../new_fields/Types'; +import { NumCast, StrCast } from '../../../new_fields/Types'; import { DragManager } from '../../util/DragManager'; import { ContentFittingDocumentView } from '../nodes/ContentFittingDocumentView'; import "./CollectionCarouselView.scss"; import { CollectionSubView } from './CollectionSubView'; import { faCaretLeft, faCaretRight } from '@fortawesome/free-solid-svg-icons'; import { Doc } from '../../../new_fields/Doc'; +import { FormattedTextBox } from '../nodes/FormattedTextBox'; @@ -45,20 +46,29 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; } + panelHeight = () => this.props.PanelHeight() - 50; @computed get content() { const index = NumCast(this.layoutDoc._itemIndex); return !(this.childLayoutPairs?.[index]?.layout instanceof Doc) ? (null) : - +
+
+ +
+
+ +
+
} @computed get buttons() { return <> -
+
-
+
; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 8cfaa5951..160279efd 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -174,6 +174,45 @@ export class CollectionDockingView extends React.Component { + if (child.contentItems.length === 1 && child.contentItems[0].config.component === "DocumentFrameRenderer" && + DocumentManager.Instance.getDocumentViewById(child.contentItems[0].config.props.documentId)?.Document.isDisplayPanle) { + child.contentItems[0].remove(); + child.addChild(newContentItem, undefined, true); + instance.layoutChanged(document); + return true; + } else { + Array.from(child.contentItems).filter((tab: any) => tab.config.component === "DocumentFrameRenderer").some((tab: any, j: number) => { + if (DocumentManager.Instance.getDocumentViewById(tab.config.props.documentId)?.Document.isDisplayPanel) { + child.contentItems[j].remove(); + child.addChild(newContentItem, undefined, true); + return true; + } + return false; + }); + } + return false; + }); + } + if (retVal) { + instance.stateChanged(); + } + return retVal; + } + // // Creates a vertical split on the right side of the docking view, and then adds the Document to the right of that split @@ -217,8 +256,9 @@ export class CollectionDockingView extends React.Component doc) { const flyout = (
{this._allFacets.map(facet => )} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 321a6d34c..65d421c52 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -70,10 +70,10 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { (args) => { const childLayout = Cast(this.props.Document.childLayout, Doc); if (childLayout instanceof Doc) { - this.childDocs.map(doc => Doc.ApplyTemplateTo(childLayout, doc, "layoutFromParent")); + this.childDocs.map(doc => Doc.ApplyTemplateTo(childLayout, doc, "layout_fromParent")); } else if (!(childLayout instanceof Promise)) { - this.childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layoutFromParent" && (doc.layoutKey = "layout")); + this.childDocs.filter(d => !d.isTemplateForField).map(doc => doc.layoutKey === "layout_fromParent" && (doc.layoutKey = "layout")); } }, { fireImmediately: true }); @@ -183,7 +183,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { if (docDragData && !docDragData.applyAsTemplate) { if (de.altKey && docDragData.draggedDocuments.length) { this.childDocs.map(doc => - Doc.ApplyTemplateTo(docDragData.draggedDocuments[0], doc, "layoutFromParent")); + Doc.ApplyTemplateTo(docDragData.draggedDocuments[0], doc, "layout_fromParent")); e.stopPropagation(); return true; } diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 6927f2856..66055d31d 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -34,6 +34,7 @@ import "./CollectionTreeView.scss"; import React = require("react"); import { CollectionViewType } from './CollectionView'; import { RichTextField } from '../../../new_fields/RichTextField'; +import { ObjectField } from '../../../new_fields/ObjectField'; export interface TreeViewProps { @@ -629,13 +630,23 @@ export class CollectionTreeView extends CollectionSubView(Document) { } ContextMenu.Instance.addItem({ description: "Buxton Layout", icon: "eye", event: () => { - const { TextDocument, ImageDocument, MulticolumnDocument, TreeDocument, CarouselDocument } = Docs.Create; + DocListCast(this.dataDoc[this.props.fieldKey]).map(d => { + DocListCast(d.data).map((img, i) => { + const caption = (d.captions as any)[i]?.data; + if (caption instanceof ObjectField) { + Doc.GetProto(img).caption = ObjectField.MakeCopy(caption as ObjectField); + } + img._hideSidebar = true; + d.captions = undefined; + }); + }); + const { TextDocument, ImageDocument, CarouselDocument } = Docs.Create; const { Document } = this.props; const fallbackImg = "http://www.cs.brown.edu/~bcz/face.gif"; const detailedTemplate = `{ "doc": { "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "short_description" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "year" } } ] }, { "type": "paragraph", "content": [ { "type": "dashField", "attrs": { "fieldKey": "company" } } ] } ] }, "selection":{"type":"text","anchor":1,"head":1},"storedMarks":[] }`; const detailedLayout = Docs.Create.StackingDocument([ - CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0 }), + CarouselDocument([], { title: "data", _height: 350, _itemIndex: 0, backgroundColor: "#9b9b9b3F" }), TextDocument("", { title: "details", _autoHeight: true, _textTemplate: new RichTextField(detailedTemplate, "short_description year company") }) ], { _chromeStatus: "disabled", title: "detailed layout stack" }); detailedLayout.isTemplateDoc = makeTemplate(detailedLayout); @@ -711,23 +722,18 @@ export class CollectionTreeView extends CollectionSubView(Document) { } Scripting.addGlobal(function readFacetData(layoutDoc: Doc, dataDoc: Doc, dataKey: string, facetHeader: string) { - const facetValues = new Set(); - DocListCast(dataDoc[dataKey]).forEach(child => { - Object.keys(Doc.GetProto(child)).forEach(key => child[key] instanceof Doc && facetValues.add((child[key] as Doc)[facetHeader]?.toString() || "(null)")); - facetValues.add(Field.toString(child[facetHeader] as Field)); - }); - const text = "determineCheckedState(layoutDoc, facetHeader, facetValue)"; - const params = { - layoutDoc: Doc.name, - facetHeader: "string", - facetValue: "string" - }; - const capturedVariables = { layoutDoc, facetHeader }; - return new List(Array.from(facetValues).sort().map(facetValue => { - const value = Docs.Create.TextDocument("", { title: facetValue.toString() }); - value.treeViewChecked = ComputedField.MakeFunction(text, params, { ...capturedVariables, facetValue }); - return value; - })); + const allCollectionDocs = DocListCast(dataDoc[dataKey]); + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => + set.add(Field.toString(child[facetHeader] as Field)), new Set())); + + const facetValueDocSet = facetValues.sort().map(facetValue => + Docs.Create.TextDocument("", { + title: facetValue.toString(), + treeViewChecked: ComputedField.MakeFunction("determineCheckedState(layoutDoc, facetHeader, facetValue)", + { layoutDoc: Doc.name, facetHeader: "string", facetValue: "string" }, + { layoutDoc, facetHeader, facetValue }) + })); + return new List(facetValueDocSet); }); Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) { diff --git a/src/client/views/nodes/FormattedTextBox.tsx b/src/client/views/nodes/FormattedTextBox.tsx index 5c9f1a754..732bbc976 100644 --- a/src/client/views/nodes/FormattedTextBox.tsx +++ b/src/client/views/nodes/FormattedTextBox.tsx @@ -1099,7 +1099,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps &
- {this.props.Document.hideSidebar ? (null) : this.sidebarWidthPercent === "0%" ? + {this.props.Document._hideSidebar ? (null) : this.sidebarWidthPercent === "0%" ?
this.toggleSidebar()} /> :
@@ -1116,7 +1116,7 @@ export class FormattedTextBox extends DocAnnotatableComponent<(FieldViewProps & whenActiveChanged={this.whenActiveChanged} removeDocument={this.removeDocument} moveDocument={this.moveDocument} - addDocument={(doc: Doc) => { doc.hideSidebar = true; return this.addDocument(doc); }} + addDocument={(doc: Doc) => { doc._hideSidebar = true; return this.addDocument(doc); }} CollectionView={undefined} ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-(this.props.PanelWidth() - this.sidebarWidth), 0)} renderDepth={this.props.renderDepth + 1} diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 16b248ae7..7cfa4696d 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -112,10 +112,6 @@ def write_collection(parse_results, display_fields, storage_key, viewType): "url": parse_results["image_urls"][0], "__type": "image" } - fields["activeHero"] = { - "url": parse_results["image_urls"][0], - "__type": "image" - } fields["isPrototype"] = True fields["page"] = -1 @@ -139,7 +135,7 @@ def write_text_doc(content): "proto": protofy(data_doc_guid), "x": 10, "y": 10, - "width": 400, + "_width": 400, "zIndex": 2 }, "__type": "Doc" @@ -154,7 +150,7 @@ def write_text_doc(content): "__type": "RichTextField" }, "title": content, - "nativeWidth": 200, + "_nativeWidth": 200, "author": "Bill Buxton", "creationDate": { "date": datetime.datetime.utcnow().microsecond, @@ -163,8 +159,8 @@ def write_text_doc(content): "isPrototype": True, "_autoHeight": True, "page": -1, - "nativeHeight": 200, - "height": 200, + "_nativeHeight": 200, + "_height": 200, "data_text": content }, "__type": "Doc" @@ -194,7 +190,7 @@ def write_image(folder, name): "proto": protofy(data_doc_guid), "x": 10, "y": 10, - "width": min(800, native_width), + "_width": min(800, native_width), "zIndex": 2, "widthUnit": "*", "widthMagnitude": 1 @@ -211,7 +207,7 @@ def write_image(folder, name): "__type": "image" }, "title": name, - "nativeWidth": native_width, + "_nativeWidth": native_width, "author": "Bill Buxton", "creationDate": { "date": datetime.datetime.utcnow().microsecond, @@ -219,8 +215,8 @@ def write_image(folder, name): }, "isPrototype": True, "page": -1, - "nativeHeight": native_height, - "height": native_height + "_nativeHeight": native_height, + "_height": native_height }, "__type": "Doc" } -- cgit v1.2.3-70-g09d2