diff options
Diffstat (limited to 'src/client/views/nodes')
| -rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 18 | ||||
| -rw-r--r-- | src/client/views/nodes/DataViz.tsx | 21 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.scss (renamed from src/client/views/nodes/DataViz.scss) | 0 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 90 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/DrawHelper.ts | 247 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/HistogramBox.scss | 18 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/HistogramBox.tsx | 159 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/TableBox.scss | 22 | ||||
| -rw-r--r-- | src/client/views/nodes/DataVizBox/TableBox.tsx | 37 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 8 | ||||
| -rw-r--r-- | src/client/views/nodes/button/FontIconBadge.tsx | 2 | ||||
| -rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 7 |
13 files changed, 590 insertions, 41 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 94cef2906..c42c2306a 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -63,7 +63,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp _recorder: any; // MediaRecorder _recordStart = 0; _pauseStart = 0; // time when recording is paused (used to keep track of recording timecodes) - _pauseEnd = 0; _pausedTime = 0; _stream: MediaStream | undefined; // passed to MediaRecorder, records device input audio _play: any = null; // timeout for playback @@ -81,7 +80,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp @computed get miniPlayer() { return this.props.PanelHeight() < 50; } // used to collapse timeline when node is shrunk @computed get links() { return DocListCast(this.dataDoc.links); } - @computed get pauseTime() { return this._pauseEnd - this._pauseStart; } // total time paused to update the correct recording time @computed get mediaState() { return this.dataDoc.mediaState as media_state; } @computed get path() { // returns the path of the audio file const path = Cast(this.props.Document[this.fieldKey], AudioField, null)?.url.href || ""; @@ -97,9 +95,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this._dropDisposer?.(); Object.values(this._disposers).forEach((disposer) => disposer?.()); - // removes doc from active recordings if recording when closed - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + this.mediaState === media_state.Recording && this.stopRecording(); } @action @@ -220,10 +216,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp updateRecordTime = () => { if (this.mediaState === media_state.Recording) { setTimeout(this.updateRecordTime, 30); - if (this._paused) { - this._pausedTime += (new Date().getTime() - this._recordStart) / 1000; - } else { - this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; + if (!this._paused) { + this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000; } } } @@ -253,7 +247,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp if (this._recorder) { this._recorder.stop(); this._recorder = undefined; - this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; + const now = new Date().getTime(); + this._paused && (this._pausedTime += now - this._pauseStart); + this.dataDoc[this.fieldKey + "-duration"] = (now - this._recordStart - this._pausedTime) / 1000; this.mediaState = media_state.Paused; this._stream?.getAudioTracks()[0].stop(); const ind = DocUtils.ActiveRecordings.indexOf(this); @@ -379,8 +375,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // continue the recording recordPlay = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => { - this._pauseEnd = new Date().getTime(); this._paused = false; + this._pausedTime += new Date().getTime() - this._pauseStart; this._recorder.resume(); }), false); } diff --git a/src/client/views/nodes/DataViz.tsx b/src/client/views/nodes/DataViz.tsx deleted file mode 100644 index d9541dba0..000000000 --- a/src/client/views/nodes/DataViz.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { observer } from "mobx-react"; -import * as React from "react"; -import { ViewBoxBaseComponent } from '../DocComponent'; -import "./DataViz.scss"; -import { FieldView, FieldViewProps } from "./FieldView"; - -@observer -export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() { - - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataVizBox, fieldKey); } - - render() { - return ( - <div > - <div> - Hi - </div> - </div> - ); - } -}
\ No newline at end of file diff --git a/src/client/views/nodes/DataViz.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss index e69de29bb..e69de29bb 100644 --- a/src/client/views/nodes/DataViz.scss +++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx new file mode 100644 index 000000000..592723ee9 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -0,0 +1,90 @@ +import { action, computed, observable } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import { StrCast } from "../../../../fields/Types"; +import { ViewBoxBaseComponent } from "../../DocComponent"; +import { FieldViewProps, FieldView } from "../FieldView"; +import "./DataVizBox.scss"; +import { HistogramBox } from "./HistogramBox"; +import { TableBox } from "./TableBox"; + +enum DataVizView { + TABLE = "table", + HISTOGRAM= "histogram" +} + + +@observer +export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() { + @observable private pairs: {x: number, y:number}[] = [{x: 1, y:2}]; + + // TODO: nda - make this use enum values instead + // @observable private currView: DataVizView = DataVizView.TABLE; + @computed get currView() { + if (this.rootDoc._dataVizView) { + return StrCast(this.rootDoc._dataVizView); + } else { + return "table"; + } + } + + constructor(props: any) { + super(props); + if (!this.rootDoc._dataVizView) { + // TODO: nda - this might not always want to default to "table" + this.rootDoc._dataVizView = "table"; + } + } + + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataVizBox, fieldKey); } + + @action + private createPairs() { + const xVals: number[] = [0, 1, 2, 3, 4, 5]; + // const yVals: number[] = [10, 20, 30, 40, 50, 60]; + const yVals: number[] = [1, 2, 3, 4, 5, 6]; + let pairs: { + x: number, + y:number + }[] = []; + if (xVals.length != yVals.length) return pairs; + for (let i = 0; i < xVals.length; i++) { + pairs.push({x: xVals[i], y: yVals[i]}); + } + this.pairs = pairs; + return pairs; + } + + @computed get selectView() { + switch(this.currView) { + case "table": + return (<TableBox pairs={this.pairs} />) + case "histogram": + return (<HistogramBox rootDoc={this.rootDoc} pairs={this.pairs}/>) + } + } + + @computed get pairVals() { + return this.createPairs(); + } + + componentDidMount() { + this.createPairs(); + } + + // handle changing the view using a button + @action changeViewHandler(e: React.MouseEvent<HTMLButtonElement>) { + e.preventDefault(); + e.stopPropagation(); + this.rootDoc._dataVizView = this.currView == "table" ? "histogram" : "table"; + } + + render() { + return ( + <div className="dataViz"> + <button onClick={(e) => this.changeViewHandler(e)}>Change View</button> + {this.selectView} + </div> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DrawHelper.ts b/src/client/views/nodes/DataVizBox/DrawHelper.ts new file mode 100644 index 000000000..595cecebf --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DrawHelper.ts @@ -0,0 +1,247 @@ +export class PIXIPoint { + public get x() { return this.coords[0]; } + public get y() { return this.coords[1]; } + public set x(value: number) { this.coords[0] = value; } + public set y(value: number) { this.coords[1] = value; } + public coords: number[] = [0, 0]; + constructor(x: number, y: number) { + this.coords[0] = x; + this.coords[1] = y; + } +} + +export class PIXIRectangle { + public x: number; + public y: number; + public width: number; + public height: number; + public get left() { return this.x; } + public get right() { return this.x + this.width; } + public get top() { return this.y; } + public get bottom() { return this.top + this.height; } + public static get EMPTY() { return new PIXIRectangle(0, 0, -1, -1); } + constructor(x: number, y: number, width: number, height: number) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } +} + +export class MathUtil { + + public static EPSILON: number = 0.001; + + public static Sign(value: number): number { + return value >= 0 ? 1 : -1; + } + + public static AddPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint { + if (inline) { + p1.x += p2.x; + p1.y += p2.y; + return p1; + } + else { + return new PIXIPoint(p1.x + p2.x, p1.y + p2.y); + } + } + + public static Perp(p1: PIXIPoint): PIXIPoint { + return new PIXIPoint(-p1.y, p1.x); + } + + public static DividePoint(p1: PIXIPoint, by: number, inline: boolean = false): PIXIPoint { + if (inline) { + p1.x /= by; + p1.y /= by; + return p1; + } + else { + return new PIXIPoint(p1.x / by, p1.y / by); + } + } + + public static MultiplyConstant(p1: PIXIPoint, by: number, inline: boolean = false) { + if (inline) { + p1.x *= by; + p1.y *= by; + return p1; + } + else { + return new PIXIPoint(p1.x * by, p1.y * by); + } + } + + public static SubtractPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint { + if (inline) { + p1.x -= p2.x; + p1.y -= p2.y; + return p1; + } + else { + return new PIXIPoint(p1.x - p2.x, p1.y - p2.y); + } + } + + public static Area(rect: PIXIRectangle): number { + return rect.width * rect.height; + } + + public static DistToLineSegment(v: PIXIPoint, w: PIXIPoint, p: PIXIPoint) { + // Return minimum distance between line segment vw and point p + var l2 = MathUtil.DistSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt + if (l2 === 0.0) return MathUtil.Dist(p, v); // v === w case + // Consider the line extending the segment, parameterized as v + t (w - v). + // We find projection of point p onto the line. + // It falls where t = [(p-v) . (w-v)] / |w-v|^2 + // We clamp t from [0,1] to handle points outside the segment vw. + var dot = MathUtil.Dot( + MathUtil.SubtractPoint(p, v), + MathUtil.SubtractPoint(w, v)) / l2; + var t = Math.max(0, Math.min(1, dot)); + // Projection falls on the segment + var projection = MathUtil.AddPoint(v, + MathUtil.MultiplyConstant( + MathUtil.SubtractPoint(w, v), t)); + return MathUtil.Dist(p, projection); + } + + public static LineSegmentIntersection(ps1: PIXIPoint, pe1: PIXIPoint, ps2: PIXIPoint, pe2: PIXIPoint): PIXIPoint | undefined { + var a1 = pe1.y - ps1.y; + var b1 = ps1.x - pe1.x; + + var a2 = pe2.y - ps2.y; + var b2 = ps2.x - pe2.x; + + var delta = a1 * b2 - a2 * b1; + if (delta === 0) { + return undefined; + } + var c2 = a2 * ps2.x + b2 * ps2.y; + var c1 = a1 * ps1.x + b1 * ps1.y; + var invdelta = 1 / delta; + return new PIXIPoint((b2 * c1 - b1 * c2) * invdelta, (a1 * c2 - a2 * c1) * invdelta); + } + + public static PointInPIXIRectangle(p: PIXIPoint, rect: PIXIRectangle): boolean { + if (p.x < rect.left - this.EPSILON) { + return false; + } + if (p.x > rect.right + this.EPSILON) { + return false; + } + if (p.y < rect.top - this.EPSILON) { + return false; + } + if (p.y > rect.bottom + this.EPSILON) { + return false; + } + + return true; + } + + public static LinePIXIRectangleIntersection(lineFrom: PIXIPoint, lineTo: PIXIPoint, rect: PIXIRectangle): Array<PIXIPoint> { + var r1 = new PIXIPoint(rect.left, rect.top); + var r2 = new PIXIPoint(rect.right, rect.top); + var r3 = new PIXIPoint(rect.right, rect.bottom); + var r4 = new PIXIPoint(rect.left, rect.bottom); + var ret = new Array<PIXIPoint>(); + var dist = this.Dist(lineFrom, lineTo); + var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2); + if (inter && this.PointInPIXIRectangle(inter, rect) && + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { + ret.push(inter); + } + inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3); + if (inter && this.PointInPIXIRectangle(inter, rect) && + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { + ret.push(inter); + } + inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4); + if (inter && this.PointInPIXIRectangle(inter, rect) && + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { + ret.push(inter); + } + inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1); + if (inter && this.PointInPIXIRectangle(inter, rect) && + this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) { + ret.push(inter); + } + return ret; + } + + public static Intersection(rect1: PIXIRectangle, rect2: PIXIRectangle): PIXIRectangle { + const left = Math.max(rect1.x, rect2.x); + const right = Math.min(rect1.x + rect1.width, rect2.x + rect2.width); + const top = Math.max(rect1.y, rect2.y); + const bottom = Math.min(rect1.y + rect1.height, rect2.y + rect2.height); + return new PIXIRectangle(left, top, right - left, bottom - top); + } + + public static Dist(p1: PIXIPoint, p2: PIXIPoint): number { + return Math.sqrt(MathUtil.DistSquared(p1, p2)); + } + + public static Dot(p1: PIXIPoint, p2: PIXIPoint): number { + return p1.x * p2.x + p1.y * p2.y; + } + + public static Normalize(p1: PIXIPoint) { + var d = this.Length(p1); + return new PIXIPoint(p1.x / d, p1.y / d); + } + + public static Length(p1: PIXIPoint): number { + return Math.sqrt(p1.x * p1.x + p1.y * p1.y); + } + + public static DistSquared(p1: PIXIPoint, p2: PIXIPoint): number { + const a = p1.x - p2.x; + const b = p1.y - p2.y; + return (a * a + b * b); + } + + public static RectIntersectsRect(r1: PIXIRectangle, r2: PIXIRectangle): boolean { + return !(r2.x > r1.x + r1.width || + r2.x + r2.width < r1.x || + r2.y > r1.y + r1.height || + r2.y + r2.height < r1.y); + } + + public static ArgMin(temp: number[]): number { + let index = 0; + let value = temp[0]; + for (let i = 1; i < temp.length; i++) { + if (temp[i] < value) { + value = temp[i]; + index = i; + } + } + return index; + } + + public static ArgMax(temp: number[]): number { + let index = 0; + let value = temp[0]; + for (let i = 1; i < temp.length; i++) { + if (temp[i] > value) { + value = temp[i]; + index = i; + } + } + return index; + } + + public static Combinations<T>(chars: T[]) { + let result = new Array<T>(); + let f = (prefix: any, chars: any) => { + for (let i = 0; i < chars.length; i++) { + result.push(prefix.concat(chars[i])); + f(prefix.concat(chars[i]), chars.slice(i + 1)); + } + }; + f([], chars); + return result; + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.scss b/src/client/views/nodes/DataVizBox/HistogramBox.scss new file mode 100644 index 000000000..5aac9dc77 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/HistogramBox.scss @@ -0,0 +1,18 @@ +// change the stroke color of line-svg class +.svgLine { + position: absolute; + background: darkGray; + stroke: #000; + stroke-width: 1px; + width:100%; + height:100%; + opacity: 0.4; +} + +.svgContainer { + position: absolute; + top:0; + left:0; + width:100%; + height: 100%; +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.tsx b/src/client/views/nodes/DataVizBox/HistogramBox.tsx new file mode 100644 index 000000000..00dc2ef46 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/HistogramBox.tsx @@ -0,0 +1,159 @@ +import { action, computed, observable } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import { Doc } from "../../../../fields/Doc"; +import { NumCast } from "../../../../fields/Types"; +import "./HistogramBox.scss"; + +interface HistogramBoxProps { + rootDoc: Doc; + pairs: { + x: number, + y: number + }[] +} + + +export class HistogramBox extends React.Component<HistogramBoxProps> { + + private origin = {x: 0.1 * this.width, y: 0.9 * this.height}; + + @computed get width() { + return NumCast(this.props.rootDoc.width); + } + + @computed get height() { + return NumCast(this.props.rootDoc.height); + } + + @computed get x() { + return NumCast(this.props.rootDoc.x); + } + + @computed get y() { + return NumCast(this.props.rootDoc.y); + } + + @computed get generatePoints() { + // evenly distribute points along the x axis + const xVals: number[] = this.props.pairs.map(p => p.x); + const yVals: number[] = this.props.pairs.map(p => p.y); + + const xMin = Math.min(...xVals); + const xMax = Math.max(...xVals); + const yMin = Math.min(...yVals); + const yMax = Math.max(...yVals); + + const xRange = xMax - xMin; + const yRange = yMax - yMin; + + const xScale = this.width / xRange; + const yScale = this.height / yRange; + + const xOffset = (this.x + (0.1 * this.width)) - xMin * xScale; + const yOffset = (this.y + (0.25 * this.height)) - yMin * yScale; + + const points: { + x: number, + y: number + }[] = this.props.pairs.map(p => { + return { + x: (p.x * xScale + xOffset) + this.origin.x, + y: (p.y * yScale + yOffset) + } + }); + + return points; + } + + @computed get generateGraphLine() { + const points = this.generatePoints; + // loop through points and create a line from each point to the next + let lines: { + x1: number, + y1: number, + x2: number, + y2: number + }[] = []; + for (let i = 0; i < points.length - 1; i++) { + lines.push({ + x1: points[i].x, + y1: points[i].y, + x2: points[i + 1].x, + y2: points[i + 1].y + }); + } + // generate array of svg with lines + let svgLines: JSX.Element[] = []; + for (let i = 0; i < lines.length; i++) { + svgLines.push( + <line + className="svgLine" + key={i} + x1={lines[i].x1} + y1={lines[i].y1} + x2={lines[i].x2} + y2={lines[i].y2} + stroke="black" + strokeWidth={2} + /> + ); + } + + let res = []; + for (let i = 0; i < svgLines.length; i++) { + res.push(<svg className="svgContainer">{svgLines[i]}</svg>) + } + return res; + } + + @computed get generateAxes() { + + const xAxis = { + x1: 0.1 * this.width, + x2: 0.9 * this.width, + y1: 0.9 * this.height, + y2: 0.9 * this.height, + }; + + const yAxis = { + x1: 0.1 * this.width, + x2: 0.1 * this.width, + y1: 0.25 * this.height, + y2: 0.9 * this.height, + }; + + + return ( + [ + (<svg className="svgContainer"> + {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={this.width - (0.1 * this.width)} y2={xAxis} /> */} + <line className="svgLine" x1={xAxis.x1} y1={xAxis.y1} x2={xAxis.x2} y2={xAxis.y2}/> + + {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={yAxis} y2={this.y + 50} /> */} + </svg>), + ( + <svg className="svgContainer"> + <line className="svgLine" x1={yAxis.x1} y1={yAxis.y1} x2={yAxis.x2} y2={yAxis.y2} /> + {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={yAxis} y2={this.y + 50} /> */} + </svg>) + ] + ) + } + + + render() { + return ( + <div>histogram box + {/* <svg className="svgContainer"> + {this.generateSVGLine} + </svg> */} + {this.generateAxes[0]} + {this.generateAxes[1]} + {this.generateGraphLine.map(line => line)} + </div> + ) + + } + +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/TableBox.scss b/src/client/views/nodes/DataVizBox/TableBox.scss new file mode 100644 index 000000000..1264d6a46 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/TableBox.scss @@ -0,0 +1,22 @@ +.table { + margin-top: 10px; + margin-bottom: 10px; + margin-left: 10px; + margin-right: 10px; +} + +.table-row { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 5px; + border-bottom: 1px solid #ccc; +} + +.table-container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/TableBox.tsx b/src/client/views/nodes/DataVizBox/TableBox.tsx new file mode 100644 index 000000000..dfa8262d8 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/TableBox.tsx @@ -0,0 +1,37 @@ +import { action, computed, observable } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; + +interface TableBoxProps { + pairs: {x: number, y:number}[] +} + + +export class TableBox extends React.Component<TableBoxProps> { + + + + render() { + return ( + <div className="table-container"> + <table className="table"> + <thead> + <tr className="table-row"> + <th>x</th> + <th>y</th> + </tr> + </thead> + <tbody> + {this.props.pairs.map(p => { + return (<tr className="table-row"> + <td>{p.x}</td> + <td>{p.y}</td> + </tr>) + })} + </tbody> + </table> + </div> + ) + } + +}
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 371d85a32..96ac3e332 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -19,7 +19,7 @@ import { AudioBox } from "./AudioBox"; import { FontIconBox } from "./button/FontIconBox"; import { ColorBox } from "./ColorBox"; import { ComparisonBox } from "./ComparisonBox"; -import { DataVizBox } from "./DataViz"; +import { DataVizBox } from "./DataVizBox/DataVizBox"; import { DocumentViewProps } from "./DocumentView"; import "./DocumentView.scss"; import { EquationBox } from "./EquationBox"; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 8d4fd376f..2ea976813 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1243,13 +1243,14 @@ export class DocumentView extends React.Component<DocumentViewProps> { @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } @computed get panelHeight() { - if (this.effectiveNativeHeight) { - return Math.min(this.props.PanelHeight(), Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight), this.effectiveNativeHeight) * this.nativeScaling); + if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) { + const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0; + return Math.min(this.props.PanelHeight(), Math.max(scrollHeight, this.effectiveNativeHeight) * this.nativeScaling); } return this.props.PanelHeight(); } @computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; } - @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 ? (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2 : 0; } + @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0; } @computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; } @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; } @@ -1343,7 +1344,6 @@ export class DocumentView extends React.Component<DocumentViewProps> { transition: this.props.dataTransition, position: this.props.Document.isInkMask ? "absolute" : undefined, transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`, - margin: this.fitWidth ? "auto" : undefined, width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`, height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` : `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`), diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx index df17d603f..3b5aac221 100644 --- a/src/client/views/nodes/button/FontIconBadge.tsx +++ b/src/client/views/nodes/button/FontIconBadge.tsx @@ -6,7 +6,7 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils import { DragManager } from "../../../util/DragManager"; import "./FontIconBadge.scss"; -interface FontIconBadgeProps { +interface FontIconBadgeProps { value: string | undefined; } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d3dee3c89..8e1698eba 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -29,7 +29,7 @@ import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from "../../../util/DragManager"; -import { makeTemplate } from '../../../util/DropConverter'; +import { MakeTemplate } from '../../../util/DropConverter'; import { LinkManager } from '../../../util/LinkManager'; import { SelectionManager } from "../../../util/SelectionManager"; import { SnappingManager } from '../../../util/SnappingManager'; @@ -682,7 +682,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (!this.layoutDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; - this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + MakeTemplate(this.rootDoc, true, title); } else if (!this.rootDoc.isTemplateDoc) { const title = StrCast(this.rootDoc.title); this.rootDoc.title = "text"; @@ -691,7 +691,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.rootDoc.isTemplateDoc = false; this.rootDoc.isTemplateForField = ""; this.rootDoc.layoutKey = "layout"; - this.rootDoc.isTemplateDoc = makeTemplate(this.rootDoc, true, title); + MakeTemplate(this.rootDoc, true, title); setTimeout(() => { this.rootDoc._autoHeight = this.layoutDoc._autoHeight; // autoHeight, width and height this.rootDoc._width = this.layoutDoc._width || 300; // are stored on the template, since we're getting rid of the old template @@ -854,6 +854,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp return this._didScroll ? this._focusSpeed : undefined; // if we actually scrolled, then return some focusSpeed } + getScrollHeight = () => this.scrollHeight; // if the scroll height has changed and we're in autoHeight mode, then we need to update the textHeight component of the doc. // Since we also monitor all component height changes, this will update the document's height. resetNativeHeight = (scrollHeight: number) => { |
