From b1b47b17955aa7fcb49d89c1bd538d4335202ed8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Sep 2021 11:52:33 -0400 Subject: changed label boxes to fit their entire text by scaling their font --- src/client/views/nodes/FunctionPlotBox.tsx | 14 +- src/client/views/nodes/LabelBigText.js | 241 +++++++++++++++++++++++++++++ src/client/views/nodes/LabelBox.scss | 6 +- src/client/views/nodes/LabelBox.tsx | 28 +++- 4 files changed, 270 insertions(+), 19 deletions(-) create mode 100644 src/client/views/nodes/LabelBigText.js (limited to 'src') diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx index b00f97236..5050fc2d2 100644 --- a/src/client/views/nodes/FunctionPlotBox.tsx +++ b/src/client/views/nodes/FunctionPlotBox.tsx @@ -1,18 +1,16 @@ -import EquationEditor from 'equation-editor-react'; import functionPlot from "function-plot"; +import { action, computed, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; +import { Doc, DocListCast } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; -import { createSchema, makeInterface, listSpec } from '../../../fields/Schema'; -import { StrCast, Cast } from '../../../fields/Types'; +import { List } from '../../../fields/List'; +import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; +import { Cast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; +import { Docs } from '../../documents/Documents'; import { ViewBoxBaseComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; -import './LabelBox.scss'; -import { DocListCast, Doc } from '../../../fields/Doc'; -import { computed, action, reaction } from 'mobx'; -import { Docs } from '../../documents/Documents'; -import { List } from '../../../fields/List'; const EquationSchema = createSchema({}); diff --git a/src/client/views/nodes/LabelBigText.js b/src/client/views/nodes/LabelBigText.js new file mode 100644 index 000000000..db43551d8 --- /dev/null +++ b/src/client/views/nodes/LabelBigText.js @@ -0,0 +1,241 @@ +/* +Brorlandi/big-text.js v1.0.0, 2017 +Adapted from DanielHoffmann/jquery-bigtext, v1.3.0, May 2014 +And from Jetroid/bigtext.js v1.0.0, September 2016 + +Usage: +BigText("#myElement",{ + rotateText: {Number}, (null) + fontSizeFactor: {Number}, (0.8) + maximumFontSize: {Number}, (null) + limitingDimension: {String}, ("both") + horizontalAlign: {String}, ("center") + verticalAlign: {String}, ("center") + textAlign: {String}, ("center") + whiteSpace: {String}, ("nowrap") +}); + + +Original Projects: +https://github.com/DanielHoffmann/jquery-bigtext +https://github.com/Jetroid/bigtext.js + +Options: + +rotateText: Rotates the text inside the element by X degrees. + +fontSizeFactor: This option is used to give some vertical spacing for letters that overflow the line-height (like 'g', 'Á' and most other accentuated uppercase letters). This does not affect the font-size if the limiting factor is the width of the parent div. The default is 0.8 + +maximumFontSize: maximum font size to use. + +limitingDimension: In which dimension the font size should be limited. Possible values: "width", "height" or "both". Defaults to both. Using this option with values different than "both" overwrites the element parent width or height. + +horizontalAlign: Where to align the text horizontally. Possible values: "left", "center", "right". Defaults to "center". + +verticalAlign: Where to align the text vertically. Possible values: "top", "center", "bottom". Defaults to "center". + +textAlign: Sets the text align of the element. Possible values: "left", "center", "right". Defaults to "center". This option is only useful if there are linebreaks (
tags) inside the text. + +whiteSpace: Sets whitespace handling. Possible values: "nowrap", "pre". Defaults to "nowrap". (Can also be set to enable wrapping but this doesn't work well.) + +Bruno Orlandi - 2017 + +Copyright (C) 2013 Daniel Hoffmann Bernardes, Ícaro Technologies +Copyright (C) 2016 Jet Holt + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +function _calculateInnerDimensions(computedStyle) { + //Calculate the inner width and height + var innerWidth; + var innerHeight; + + var width = parseInt(computedStyle.getPropertyValue("width")); + var height = parseInt(computedStyle.getPropertyValue("height")); + var paddingLeft = parseInt(computedStyle.getPropertyValue("padding-left")); + var paddingRight = parseInt(computedStyle.getPropertyValue("padding-right")); + var paddingTop = parseInt(computedStyle.getPropertyValue("padding-top")); + var paddingBottom = parseInt(computedStyle.getPropertyValue("padding-bottom")); + var borderLeft = parseInt(computedStyle.getPropertyValue("border-left-width")); + var borderRight = parseInt(computedStyle.getPropertyValue("border-right-width")); + var borderTop = parseInt(computedStyle.getPropertyValue("border-top-width")); + var borderBottom = parseInt(computedStyle.getPropertyValue("border-bottom-width")); + + //If box-sizing is border-box, we need to subtract padding and border. + var parentBoxSizing = computedStyle.getPropertyValue("box-sizing"); + if (parentBoxSizing == "border-box") { + innerWidth = width - (paddingLeft + paddingRight + borderLeft + borderRight); + innerHeight = height - (paddingTop + paddingBottom + borderTop + borderBottom); + } else { + innerWidth = width; + innerHeight = height; + } + var obj = {}; + obj["width"] = innerWidth; + obj["height"] = innerHeight; + return obj; +} + +export default function BigText(element, options) { + + if (typeof element === 'string') { + element = document.querySelector(element); + } else if (element.length) { + // Support for array based queries (such as jQuery) + element = element[0]; + } + + var defaultOptions = { + rotateText: null, + fontSizeFactor: 0.8, + maximumFontSize: null, + limitingDimension: "both", + horizontalAlign: "center", + verticalAlign: "center", + textAlign: "center", + whiteSpace: "nowrap" + }; + + //Merge provided options and default options + options = options || {}; + for (var opt in defaultOptions) + if (defaultOptions.hasOwnProperty(opt) && !options.hasOwnProperty(opt)) + options[opt] = defaultOptions[opt]; + + //Get variables which we will reference frequently + var style = element.style; + var computedStyle = document.defaultView.getComputedStyle(element); + var parent = element.parentNode; + var parentStyle = parent.style; + var parentComputedStyle = document.defaultView.getComputedStyle(parent); + + //hides the element to prevent "flashing" + style.visibility = "hidden"; + + //Set some properties + style.display = "inline-block"; + style.clear = "both"; + style.float = "left"; + style.fontSize = (1000 * options.fontSizeFactor) + "px"; + style.lineHeight = "1000px"; + style.whiteSpace = options.whiteSpace; + style.textAlign = options.textAlign; + style.position = "relative"; + style.padding = 0; + style.margin = 0; + style.left = "50%"; + style.top = "50%"; + + //Get properties of parent to allow easier referencing later. + var parentPadding = { + top: parseInt(parentComputedStyle.getPropertyValue("padding-top")), + right: parseInt(parentComputedStyle.getPropertyValue("padding-right")), + bottom: parseInt(parentComputedStyle.getPropertyValue("padding-bottom")), + left: parseInt(parentComputedStyle.getPropertyValue("padding-left")), + }; + var parentBorder = { + top: parseInt(parentComputedStyle.getPropertyValue("border-top")), + right: parseInt(parentComputedStyle.getPropertyValue("border-right")), + bottom: parseInt(parentComputedStyle.getPropertyValue("border-bottom")), + left: parseInt(parentComputedStyle.getPropertyValue("border-left")), + }; + + //Calculate the parent inner width and height + var parentInnerDimensions = _calculateInnerDimensions(parentComputedStyle); + var parentInnerWidth = parentInnerDimensions["width"]; + var parentInnerHeight = parentInnerDimensions["height"]; + + var box = { + width: element.offsetWidth, //Note: This is slightly larger than the jQuery version + height: element.offsetHeight, + }; + + + if (options.rotateText !== null) { + if (typeof options.rotateText !== "number") + throw "bigText error: rotateText value must be a number"; + var rotate = "rotate(" + options.rotateText + "deg)"; + style.webkitTransform = rotate; + style.msTransform = rotate; + style.MozTransform = rotate; + style.OTransform = rotate; + style.transform = rotate; + //calculating bounding box of the rotated element + var sine = Math.abs(Math.sin(options.rotateText * Math.PI / 180)); + var cosine = Math.abs(Math.cos(options.rotateText * Math.PI / 180)); + box.width = element.offsetWidth * cosine + element.offsetHeight * sine; + box.height = element.offsetWidth * sine + element.offsetHeight * cosine; + } + + var widthFactor = (parentInnerWidth - parentPadding.left - parentPadding.right) / box.width; + var heightFactor = (parentInnerHeight - parentPadding.top - parentPadding.bottom) / box.height; + var lineHeight; + + if (options.limitingDimension.toLowerCase() === "width") { + lineHeight = Math.floor(widthFactor * 1000); + parentStyle.height = lineHeight + "px"; + } else if (options.limitingDimension.toLowerCase() === "height") { + lineHeight = Math.floor(heightFactor * 1000); + } else if (widthFactor < heightFactor) + lineHeight = Math.floor(widthFactor * 1000); + else if (widthFactor >= heightFactor) + lineHeight = Math.floor(heightFactor * 1000); + + var fontSize = lineHeight * options.fontSizeFactor; + if (options.maximumFontSize !== null && fontSize > options.maximumFontSize) { + fontSize = options.maximumFontSize; + lineHeight = fontSize / options.fontSizeFactor; + } + + style.fontSize = Math.floor(fontSize) + "px"; + style.lineHeight = Math.ceil(lineHeight) + "px"; + style.marginBottom = "0px"; + style.marginRight = "0px"; + + if (options.limitingDimension.toLowerCase() === "height") { + //this option needs the font-size to be set already so computedStyle.getPropertyValue("width") returns the right size + //this +4 is to compensate the rounding erros that can occur due to the calls to Math.floor in the centering code + parentStyle.width = (parseInt(computedStyle.getPropertyValue("width")) + 4) + "px"; + } + + //Calculate the inner width and height + var innerDimensions = _calculateInnerDimensions(computedStyle); + var innerWidth = innerDimensions["width"]; + var innerHeight = innerDimensions["height"]; + + switch (options.verticalAlign.toLowerCase()) { + case "top": + style.top = "0%"; + break; + case "bottom": + style.top = "100%"; + style.marginTop = Math.floor(-innerHeight) + "px"; + break; + default: + style.marginTop = Math.ceil((-innerHeight / 2)) + "px"; + break; + } + + switch (options.horizontalAlign.toLowerCase()) { + case "left": + style.left = "0%"; + break; + case "right": + style.left = "100%"; + style.marginLeft = Math.floor(-innerWidth) + "px"; + break; + default: + style.marginLeft = Math.ceil((-innerWidth / 2)) + "px"; + break; + } + + //shows the element after the work is done + style.visibility = "visible"; + + return element; +} diff --git a/src/client/views/nodes/LabelBox.scss b/src/client/views/nodes/LabelBox.scss index 1290193d0..6a0d651d2 100644 --- a/src/client/views/nodes/LabelBox.scss +++ b/src/client/views/nodes/LabelBox.scss @@ -9,10 +9,10 @@ .labelBox-mainButton { max-width: 100%; - width: fit-content; - height: max-content; + width: 100%; + height: 100%; border-radius: inherit; - letter-spacing: 2px; + //letter-spacing: 2px; // bcz: doesn't work with LabelBigText text-transform: uppercase; overflow: hidden; display: inline-block; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index db1ae0537..781553dd8 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -1,19 +1,20 @@ -import { action, computed, observable, runInAction } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { List } from '../../../fields/List'; import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, StrCast } from '../../../fields/Types'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxBaseComponent } from '../DocComponent'; +import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; +import BigText from './LabelBigText'; import './LabelBox.scss'; -import { StyleProp } from '../StyleProvider'; const LabelSchema = createSchema({}); @@ -94,13 +95,24 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro fontFamily: StrCast(this.layoutDoc._fontFamily) || "inherit", letterSpacing: StrCast(this.layoutDoc.letterSpacing), textTransform: StrCast(this.layoutDoc.textTransform) as any, - paddingLeft: NumCast(this.layoutDoc._xPadding), - paddingRight: NumCast(this.layoutDoc._xPadding), - paddingTop: NumCast(this.layoutDoc._yPadding), - paddingBottom: NumCast(this.layoutDoc._yPadding), + width: this.props.PanelWidth(), + height: this.props.PanelHeight(), whiteSpace: this.layoutDoc._singleLine ? "pre" : "pre-wrap" }} > - {label.startsWith("#") ? (null) : label} + { + if (r) { + BigText(r, { + rotateText: null, + fontSizeFactor: 1, + maximumFontSize: null, + limitingDimension: "both", + horizontalAlign: "center", + verticalAlign: "center", + textAlign: "center", + whiteSpace: "nowrap" + }) + } + }}>{label.startsWith("#") ? (null) : label}
{!missingParams?.length ? (null) : missingParams.map(m =>
{m}
)} -- cgit v1.2.3-70-g09d2 From 904f9a88e78f4ce4e19c9c9a1c5f174cf3dbfac7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 24 Sep 2021 09:51:24 -0400 Subject: updated titling of LabelBox to show custom titles when they are set. --- src/client/views/nodes/LabelBox.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 781553dd8..c32aaeb2e 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -38,7 +38,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro } getTitle() { - return this.props.label || ""; + return this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) : + this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); } protected createDropTarget = (ele: HTMLDivElement) => { @@ -82,7 +83,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []); const missingParams = params?.filter(p => !this.paramsDoc[p]); params?.map(p => DocListCast(this.paramsDoc[p])); // bcz: really hacky form of prefetching ... - const label = this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); + const label = this.getTitle(); return (
this._mouseOver = false)} @@ -91,7 +92,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro style={{ boxShadow: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow) }}>
Date: Fri, 24 Sep 2021 12:16:57 -0400 Subject: label box minor tweak. --- src/client/views/nodes/LabelBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index c32aaeb2e..3e75af2a1 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -38,8 +38,8 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro } getTitle() { - return this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) : - this.props.label ? this.props.label : typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); + return this.props.label ? this.props.label : this.rootDoc["title-custom"] ? StrCast(this.rootDoc.title) : + typeof this.rootDoc[this.fieldKey] === "string" ? StrCast(this.rootDoc[this.fieldKey]) : StrCast(this.rootDoc.title); } protected createDropTarget = (ele: HTMLDivElement) => { -- cgit v1.2.3-70-g09d2 From e7e67ff7e3170b6eb5bf05ffe284ca08f4802459 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 12:30:35 -0400 Subject: added a hacky fix to what seems to be a Chrome bug when auto expanding the left flyout panel --- src/client/views/MainView.tsx | 6 ++++++ src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 35c5801e5..7edcd6217 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -467,7 +467,13 @@ export class MainView extends React.Component { } expandFlyout = action((button: Doc) => { + // bcz: What's going on here!? + // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab. All of the div below the CollectionFreeFormView's main div + // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be. + // interactively adjusting the flyout fixes the problem. So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);) this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250); + setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0); + this._sidebarContent.proto = button.target as any; this.LastButton = button; }); diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 81f6307d1..24a7d77e0 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -646,8 +646,7 @@ export class MarqueeView extends React.Component e.preventDefault()} -- cgit v1.2.3-70-g09d2 From eed7f32cda030d9ef2d8aaae168c11832460a653 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 16:09:21 -0400 Subject: fix for some youtube urls that didn't upload. --- src/client/views/collections/CollectionSubView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index cb8b55cb2..e662f3ddf 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -356,7 +356,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: const batch = UndoManager.StartBatch("youtube upload"); const generatedDocuments: Doc[] = []; - this.slowLoadDocuments((uriList || text).split("v=")[1], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); + this.slowLoadDocuments((uriList || text).split("v=")[1].split("&")[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); return; } -- cgit v1.2.3-70-g09d2 From 78f1befc4551d963ed2a95d30d09db515917a8bb Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 22:01:38 -0400 Subject: from last --- src/client/views/nodes/DocumentView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 246d9f68d..6a7cd108a 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -90,6 +90,7 @@ export interface DocComponentView { getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; + Pause?: () => void; setFocus?: () => void; componentUI?: (boundsLeft: number, boundsTop: number) => JSX.Element; fieldKey?: string; @@ -853,7 +854,7 @@ export class DocumentViewInternal extends DocComponent {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints} - {this.hideLinkButton ? (null) : + {this.hideLinkButton || this.props.renderDepth === -1 ? (null) :
} -- cgit v1.2.3-70-g09d2 From 27baf4ef57b2816b3c5147f38eddc9fac2ed6907 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 25 Sep 2021 22:04:14 -0400 Subject: fixed warning --- src/client/views/nodes/LabelBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 3e75af2a1..90b9ce55d 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -111,7 +111,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro verticalAlign: "center", textAlign: "center", whiteSpace: "nowrap" - }) + }); } }}>{label.startsWith("#") ? (null) : label}
-- cgit v1.2.3-70-g09d2 From 67703bcbc6dbc01e360d07de19648a9faab4899b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 26 Sep 2021 23:34:32 -0400 Subject: don't delete tab if it's selected and backspace is typed. --- src/client/views/GlobalKeyHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index de6f4ae8b..574d28b8f 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -141,7 +141,7 @@ export class KeyManager { case "delete": case "backspace": if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") { - const selected = SelectionManager.Views().slice(); + const selected = SelectionManager.Views().filter(dv => !dv.topMost);; UndoManager.RunInBatch(() => { SelectionManager.DeselectAll(); selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document)); -- cgit v1.2.3-70-g09d2 From f70aa7ba42153b2c283b7b9af04a5e75ea19a7ad Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Sep 2021 09:49:26 -0400 Subject: made docs with no author Admin for everyone. fixed run-time warnings: not memoized accesses when panning, NaNs drawing some labelBoxes, filter box filter boolean options, --- .../collectionFreeForm/CollectionFreeFormView.tsx | 20 +++++++++++++------- src/client/views/nodes/DocumentView.tsx | 6 +++--- src/client/views/nodes/FilterBox.tsx | 6 +++--- src/fields/util.ts | 3 ++- 4 files changed, 21 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 5b9f6139f..b2db1168d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,4 +1,4 @@ -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, ObservableMap } from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { DateField } from "../../../../fields/DateField"; @@ -811,14 +811,14 @@ export class CollectionFreeFormView extends CollectionSubView pair.layout instanceof Doc).map(pair => pair.layout); - const measuredDocs = docs.filter(doc => doc && this.childDataProvider(doc, "") && this.childSizeProvider(doc, "")). - map(doc => ({ ...this.childDataProvider(doc, ""), ...this.childSizeProvider(doc, "") })); + const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc); + const measuredDocs = docs.map(doc => ({ pos: this.childPositionProviderUnmemoized(doc, ""), size: this.childSizeProviderUnmemoized(doc, "") })) + .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! })); if (measuredDocs.length) { - const ranges = measuredDocs.reduce(({ xrange, yrange }, { x, y, width, height }) => // computes range of content + const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) => // computes range of content ({ - xrange: { min: Math.min(xrange.min, x), max: Math.max(xrange.max, x + width) }, - yrange: { min: Math.min(yrange.min, y), max: Math.max(yrange.max, y + height) } + xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) }, + yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) } }) , { xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }, @@ -1105,10 +1105,16 @@ export class CollectionFreeFormView extends CollectionSubView { + return this._layoutPoolData.get(doc[Id] + (replica || "")); + } childDataProvider = computedFn(function childDataProvider(this: any, doc: Doc, replica: string) { return this._layoutPoolData.get(doc[Id] + (replica || "")); }.bind(this)); + childSizeProviderUnmemoized = (doc: Doc, replica: string) => { + return this._layoutSizeData.get(doc[Id] + (replica || "")); + } childSizeProvider = computedFn(function childSizeProvider(this: any, doc: Doc, replica: string) { return this._layoutSizeData.get(doc[Id] + (replica || "")); }.bind(this)); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 6a7cd108a..1187521b3 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1147,10 +1147,10 @@ export class DocumentView extends React.Component { @computed get nativeScaling() { if (this.shouldNotScale) return 1; const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0; - if (this.fitWidth || this.props.PanelHeight() / this.effectiveNativeHeight > this.props.PanelWidth() / this.effectiveNativeWidth) { - return Math.max(minTextScale, this.props.PanelWidth() / this.effectiveNativeWidth); // width-limited or fitWidth + if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) { + return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth } - return Math.max(minTextScale, this.props.PanelHeight() / this.effectiveNativeHeight); // height-limited or unscaled + return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled } @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); } diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index e9f19bf9e..2041c7399 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -377,9 +377,9 @@ export class FilterBox extends ViewBoxBaseComponent
- + +
filters together
diff --git a/src/fields/util.ts b/src/fields/util.ts index 439c4d333..3590c2dea 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -191,7 +191,8 @@ let HierarchyMapping: Map | undefined; function getEffectiveAcl(target: any, user?: string): symbol { const targetAcls = target[AclSym]; const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group - if (userChecked === (target.__fields?.author || target.author)) return AclAdmin; // target may be a Doc of Proxy, so check __fields.author and .author + const targetAuthor = (target.__fields?.author || target.author); // target may be a Doc of Proxy, so check __fields.author and .author + if (userChecked === targetAuthor || !targetAuthor) return AclAdmin; if (SnappingManager.GetCachedGroupByName("Admin")) return AclAdmin; if (targetAcls && Object.keys(targetAcls).length) { -- cgit v1.2.3-70-g09d2 From 9d062a00812ee629c9af3e776db98cc184e746f9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Sep 2021 10:17:38 -0400 Subject: improved image resizing flickering caused by Chrome bug with overflow:auto. works now when fitWidth is false. --- src/client/views/nodes/ImageBox.scss | 1 - src/client/views/nodes/ImageBox.tsx | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'src') diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 2536dbe16..9142a68ed 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -101,7 +101,6 @@ margin: 0 auto; display: flex; height: 100%; - overflow: auto; .imageBox-fadeBlocker { width: 100%; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index b41bfd3ea..b927e98db 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -81,11 +81,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent this._curSuffix = forceFull ? "_o" : scrSize < 100 ? "_s" : scrSize < 400 ? "_m" : scrSize < 800 || !selected ? "_l" : "_o", { fireImmediately: true, delay: 1000 }); - this._disposers.selection = reaction(() => this.props.isSelected(), - selected => !selected && setTimeout(() => { - // Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - // this._savedAnnotations.clear(); - })); this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }), ({ nativeSize, width }) => { if (!this.layoutDoc._height) { @@ -291,7 +286,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent -
+
Date: Mon, 27 Sep 2021 14:06:51 -0400 Subject: videoBox fitsWidth properly in lightbox. documentdecorations less hacky for deciding when to allow nativewidth/height changes by adding props to document type templates. fixed height of properties pane & updated CSS for leftflyout & properties pane. marquees no longer oveflow image/video boxes. --- src/client/documents/Documents.ts | 12 +++++++++--- src/client/views/DocumentDecorations.tsx | 29 +++++++++++------------------ src/client/views/GlobalKeyHandler.ts | 2 +- src/client/views/MainView.scss | 9 ++++----- src/client/views/MainView.tsx | 12 ++++++------ src/client/views/nodes/ImageBox.scss | 1 + src/client/views/nodes/ImageBox.tsx | 4 ++-- src/client/views/nodes/VideoBox.scss | 15 ++++++++++++++- src/client/views/nodes/VideoBox.tsx | 22 +++++++++++++++------- src/client/views/nodes/WebBox.tsx | 8 ++++---- 10 files changed, 67 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8ac647b99..403620bdd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -352,7 +352,7 @@ export namespace Docs { const TemplateMap: TemplateMap = new Map([ [DocumentType.RTF, { layout: { view: FormattedTextBox, dataField: "text" }, - options: { _height: 150, _xMargin: 10, _yMargin: 10, links: ComputedField.MakeFunction("links(self)") as any } + options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.SEARCH, { layout: { view: SearchBox, dataField: defaultDataKey }, @@ -372,7 +372,7 @@ export namespace Docs { }], [DocumentType.WEB, { layout: { view: WebBox, dataField: defaultDataKey }, - options: { _height: 300, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any } + options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.COL, { layout: { view: CollectionView, dataField: defaultDataKey }, @@ -392,7 +392,7 @@ export namespace Docs { }], [DocumentType.PDF, { layout: { view: PDFBox, dataField: defaultDataKey }, - options: { _curPage: 1, _fitWidth: true, links: ComputedField.MakeFunction("links(self)") as any } + options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any } }], [DocumentType.IMPORT, { layout: { view: DirectoryImportBox, dataField: defaultDataKey }, @@ -506,6 +506,12 @@ export namespace Docs { const prototypeIds = Object.values(DocumentType).filter(type => type !== DocumentType.NONE).map(type => type + suffix); // fetch the actual prototype documents from the server const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds); + Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeHeightUnfrozen = true; // to avoid having to recreate the DB + Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeHeightUnfrozen = true; + Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeHeightUnfrozen = true; + Cast(actualProtos[DocumentType.WEB + suffix], Doc, null).nativeDimModifiable = true; // to avoid having to recreate the DB + Cast(actualProtos[DocumentType.PDF + suffix], Doc, null).nativeDimModifiable = true; + Cast(actualProtos[DocumentType.RTF + suffix], Doc, null).nativeDimModifiable = true; // update this object to include any default values: DocumentOptions for all prototypes prototypeIds.map(id => { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index e9a54d6a5..bd61c9f60 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -319,12 +319,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P let height = (doc._height || (nheight / nwidth * width)); height = !height || isNaN(height) ? 20 : height; const scale = docView.props.ScreenToLocalTransform().Scale; - const canModifyNativeDim = e.ctrlKey || doc.allowReflow; + const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable; if (nwidth && nheight) { if (nwidth / nheight !== width / height && !dragBottom) { height = nheight / nwidth * width; } - if (canModifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction + if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth; else dW = dH * nwidth / nheight; } @@ -334,34 +334,27 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P doc.x = (doc.x || 0) + dX * (actualdW - width); doc.y = (doc.y || 0) + dY * (actualdH - height); const fixedAspect = (nwidth && nheight); - if (canModifyNativeDim && [DocumentType.IMG, DocumentType.SCREENSHOT, DocumentType.VID].includes(doc.type as DocumentType)) { - dW !== 0 && runInAction(() => { - const dataDoc = doc[DataSym]; - const nw = Doc.NativeWidth(dataDoc); - const nh = Doc.NativeHeight(dataDoc); - Doc.SetNativeWidth(dataDoc, nw + (dW > 0 ? 10 : -10)); - Doc.SetNativeHeight(dataDoc, nh + (dW > 0 ? 10 : -10) * nh / nw); - }); - } - else if (fixedAspect) { - if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !canModifyNativeDim)) || dragRight) { - if (dragRight && canModifyNativeDim) { + if (fixedAspect) { + if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) { + if (dragRight && modifyNativeDim) { doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc); } else { if (!doc._fitWidth) doc._height = nheight / nwidth * actualdW; - else if (!canModifyNativeDim || dragBotRight) doc._height = actualdH; + else if (!modifyNativeDim || dragBotRight) doc._height = actualdH; } doc._width = actualdW; } else { - if (dragBottom && (canModifyNativeDim || docView.layoutDoc._fitWidth)) { // frozen web pages and others that fitWidth can't grow horizontally to match a vertical resize so the only choice is to change the nativeheight even if the ctrl key isn't used + if (dragBottom && (modifyNativeDim || + (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height. But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used) doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc); doc._autoHeight = false; } else { if (!doc._fitWidth) doc._width = nwidth / nheight * actualdH; - else if (!canModifyNativeDim || dragBotRight) doc._width = actualdW; + else if (!modifyNativeDim || dragBotRight) doc._width = actualdW; } - doc._height = actualdH; + if (!modifyNativeDim) doc._height = Math.min(nheight / nwidth * NumCast(doc._width), actualdH); + else doc._height = actualdH; } } else { dH && (doc._height = actualdH); diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 574d28b8f..364bf05e2 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -141,7 +141,7 @@ export class KeyManager { case "delete": case "backspace": if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") { - const selected = SelectionManager.Views().filter(dv => !dv.topMost);; + const selected = SelectionManager.Views().filter(dv => !dv.topMost); UndoManager.RunInBatch(() => { SelectionManager.DeselectAll(); selected.map(dv => !dv.props.Document._stayInCollection && dv.props.removeDocument?.(dv.props.Document)); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index 7fa841002..c3cdb7dde 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -109,9 +109,9 @@ .properties-container { height: 100%; - position: relative; - left: 100%; - top: calc(-100% - 36px); + position: absolute; + right: 0; + top: 0; z-index: 3000; } @@ -298,9 +298,8 @@ width: var(--flyoutHandleWidth); height: 55px; top: 50%; - left: -10px; border-radius: 8px; - position: relative; + position: absolute; z-index: 41; // lm_maximised has a z-index of 40 and this needs to be above that touch-action: none; cursor: grab; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 7edcd6217..3f7df705f 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -426,23 +426,23 @@ export class MainView extends React.Component { } @computed get mainInnerContent() { - const width = this.propertiesWidth() + this._leftMenuFlyoutWidth + this.leftMenuWidth(); - const transform = this._leftMenuFlyoutWidth ? 'translate(-28px, 0px)' : undefined; + const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); + const width = this.propertiesWidth() + leftMenuFlyoutWidth; return <> {this.leftMenuPanel}
{this.flyout} -
+
-
+
{this.dockingContent} -
+
-
+
{this.propertiesWidth() < 10 ? (null) : }
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 9142a68ed..4238f6d29 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -13,6 +13,7 @@ width: 100%; pointer-events: none; mix-blend-mode: multiply; // bcz: makes text fuzzy! + overflow: hidden; } .imageBox-fader { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index b927e98db..89f70985c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -354,15 +354,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent ; + return
; } marqueeDown = (e: React.PointerEvent) => { @@ -568,10 +568,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent [this.youtubeVideoId ? this.youtubeContent : this.content]; scaling = () => this.props.scaling?.() || 1; - panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100; - panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100; + panelWidth = (): number => this.fitWidth ? this.props.PanelWidth() : (Doc.NativeAspect(this.rootDoc) || 1) * this.panelHeight(); + panelHeight = (): number => this.fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.heightPercent / 100 * this.props.PanelHeight(); screenToLocalTransform = () => { const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling(); return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent); @@ -585,10 +586,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent { e.stopPropagation(); e.preventDefault(); }}>
-
+
- {this.uIButtons} {this.annotationLayer} - {this.renderTimeline} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) : } + {this.renderTimeline} + {this.uIButtons}
); } diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 8e6586735..37d716993 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -430,9 +430,9 @@ export class WebBox extends ViewBoxAnnotatableComponent this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" }); funcs.push({ - description: (!this.layoutDoc.allowReflow ? "Allow" : "Prevent") + " Reflow", event: () => { - const nw = !this.layoutDoc.allowReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); - this.layoutDoc.allowReflow = !nw; + description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => { + const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1); + this.layoutDoc.forceReflow = !nw; if (nw) { Doc.SetInPlace(this.layoutDoc, this.fieldKey + "-nativeWidth", nw, true); } @@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent + style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}> {this.urlContent}
; } -- cgit v1.2.3-70-g09d2 From eb529611c97c9936577697b829c50b4ca0736c6e Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 27 Sep 2021 14:32:40 -0400 Subject: fixed sharing panel css to avoid overlaps --- src/client/util/SharingManager.scss | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/client/util/SharingManager.scss b/src/client/util/SharingManager.scss index 9dc57dd1e..2de636f21 100644 --- a/src/client/util/SharingManager.scss +++ b/src/client/util/SharingManager.scss @@ -40,7 +40,6 @@ .permissions-select { z-index: 1; - margin-left: -115; border: none; outline: none; text-align: justify; // for Edge -- cgit v1.2.3-70-g09d2