aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/goldenLayout.js14
-rw-r--r--src/client/util/CurrentUserUtils.ts31
-rw-r--r--src/client/util/DragManager.ts4
-rw-r--r--src/client/util/LinkFollower.ts25
-rw-r--r--src/client/util/ServerStats.tsx4
-rw-r--r--src/client/util/SettingsManager.tsx43
-rw-r--r--src/client/views/DocumentDecorations.tsx13
-rw-r--r--src/client/views/InkingStroke.tsx5
-rw-r--r--src/client/views/LightboxView.scss11
-rw-r--r--src/client/views/LightboxView.tsx106
-rw-r--r--src/client/views/MainView.scss96
-rw-r--r--src/client/views/MainView.tsx15
-rw-r--r--src/client/views/MarqueeAnnotator.tsx4
-rw-r--r--src/client/views/PreviewCursor.tsx1
-rw-r--r--src/client/views/PropertiesButtons.tsx12
-rw-r--r--src/client/views/StyleProvider.tsx26
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.scss7
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx33
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss23
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx16
-rw-r--r--src/client/views/collections/CollectionDockingView.scss391
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx57
-rw-r--r--src/client/views/collections/CollectionSubView.tsx5
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx4
-rw-r--r--src/client/views/collections/TabDocView.tsx1
-rw-r--r--src/client/views/collections/TreeView.scss4
-rw-r--r--src/client/views/collections/TreeView.tsx8
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx9
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
-rw-r--r--src/client/views/global/globalCssVariables.scss2
-rw-r--r--src/client/views/nodes/DocumentView.tsx2
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx3
-rw-r--r--src/client/views/nodes/formattedText/DashDocView.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx7
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx12
-rw-r--r--src/fields/Doc.ts29
-rw-r--r--src/mobile/MobileInterface.tsx5
-rw-r--r--src/server/ApiManagers/UploadManager.ts30
-rw-r--r--src/server/server_Initialization.ts10
42 files changed, 723 insertions, 358 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 254be67b9..f7ceef4f4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1042,7 +1042,7 @@ export namespace Docs {
I[Initializing] = true;
I.type = DocumentType.INK;
I.layout = InkingStroke.LayoutString('stroke');
- I.layout_fitWidth = true;
+ I.layout_fitWidth = false;
I.layout_hideDecorationTitle = true; // don't show title when selected
// I.layout_hideOpenButton = true; // don't show open full screen button when selected
I.color = color;
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 843b8bb5f..00ec5e439 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -1606,10 +1606,10 @@
dragProxyHeight: 200
},
labels: {
- close: 'close',
- maximise: 'maximise',
+ close: 'close tab stack',
+ maximise: 'maximize stack',
minimise: 'minimise',
- popout: 'new tab',
+ popout: 'create new collection tab',
popin: 'pop in',
tabDropdown: 'additional tabs'
}
@@ -2922,7 +2922,7 @@
* @type {String}
*/
lm.controls.Tab._template = '<li class="lm_tab"><i class="lm_left"></i>' +
- '<div class="lm_title_wrap"><input class="lm_title"/></div><div class="lm_close_tab"></div>' +
+ '<div class="lm_title_wrap"><input class="lm_title"/></div><div class="lm_close_tab" title="close tab"></div>' +
'<i class="lm_right"></i></li>';
lm.utils.copy(lm.controls.Tab.prototype, {
@@ -5083,10 +5083,10 @@
'column',
'stack',
'component',
- 'close',
- 'maximise',
+ 'close tab stack',
+ 'maximize stack',
'minimise',
- 'new tab'
+ 'create new collection tab'
];
};
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 452bb74cd..2ea5972ee 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -8,10 +8,10 @@ import { RichTextField } from "../../fields/RichTextField";
import { listSpec } from "../../fields/Schema";
import { ScriptField } from "../../fields/ScriptField";
import { Cast, DateCast, DocCast, StrCast } from "../../fields/Types";
-import { nullAudio, URLField, WebField } from "../../fields/URLField";
+import { nullAudio, WebField } from "../../fields/URLField";
import { SetCachedGroups, SharingPermissions } from "../../fields/util";
import { GestureUtils } from "../../pen-gestures/GestureUtils";
-import { addStyleSheetRule, OmitKeys, Utils } from "../../Utils";
+import { OmitKeys, Utils } from "../../Utils";
import { DocServer } from "../DocServer";
import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
@@ -19,7 +19,7 @@ import { TreeViewType } from "../views/collections/CollectionTreeView";
import { DashboardView } from "../views/DashboardView";
import { Colors } from "../views/global/globalEnums";
import { media_state } from "../views/nodes/AudioBox";
-import { OpenWhere } from "../views/nodes/DocumentView";
+import { DocumentView, OpenWhere } from "../views/nodes/DocumentView";
import { ButtonType } from "../views/nodes/FontIconBox/FontIconBox";
import { ImportElementBox } from "../views/nodes/importBox/ImportElementBox";
import { OverlayView } from "../views/OverlayView";
@@ -28,7 +28,7 @@ import { MakeTemplate } from "./DropConverter";
import { FollowLinkScript } from "./LinkFollower";
import { LinkManager } from "./LinkManager";
import { ScriptingGlobals } from "./ScriptingGlobals";
-import { ColorScheme, SettingsManager } from "./SettingsManager";
+import { ColorScheme } from "./SettingsManager";
import { UndoManager } from "./UndoManager";
interface Button {
@@ -476,7 +476,6 @@ export class CurrentUserUtils {
static setupDashboards(doc: Doc, field:string) {
var myDashboards = DocCast(doc[field]);
- const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
const newDashboard = `createNewDashboard()`;
const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _dragOnlyWithinContainer: true, _layout_hideContextMenu: true,
@@ -487,14 +486,14 @@ export class CurrentUserUtils {
const contextMenuScripts = [/*newDashboard*/] as string[];
const contextMenuLabels = [/*"Create New Dashboard"*/] as string[];
const contextMenuIcons = [/*"plus"*/] as string[];
- const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)', 'resetDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
- const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, '!IsNoviceMode()'];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
- const childContextMenuLabels = ["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
- const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
+ const childContextMenuScripts = [`toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)', 'resetDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
+ const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, '!IsNoviceMode()'];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
+ const childContextMenuLabels = ["Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
+ const childContextMenuIcons = ["tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
const reqdOpts:DocumentOptions = {
title: "My Dashboards", childHideLinkButton: true, treeView_FreezeChildren: "remove|add", treeView_HideTitle: true, layout_boxShadow: "0 0", childDontRegisterViews: true,
- dropAction: "same", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeView_TruncateTitleWidth: 350, ignoreClick: true,
- layout_headerButton: newDashboardButton, childDragAction: "none",
+ dropAction: "inSame", treeView_Type: TreeViewType.fileSystem, isFolder: true, isSystem: true, treeView_TruncateTitleWidth: 350, ignoreClick: true,
+ layout_headerButton: newDashboardButton, childDragAction: "inSame",
_layout_showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
contextMenuLabels:new List<string>(contextMenuLabels),
contextMenuIcons:new List<string>(contextMenuIcons),
@@ -708,14 +707,14 @@ export class CurrentUserUtils {
CollectionViewType.Grid, CollectionViewType.NoteTaking]),
title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
{ title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}},
- { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
{ title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}},
+ { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected
{ title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform
{ title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
{ title: "Num", icon:"", toolTip: "Frame Number (click to toggle edit mode)", btnType: ButtonType.TextButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)', buttonText: 'selectedDocs()?.lastElement()?.currentFrame?.toString()'}, width: 20, scripts: { onClick: '{ return curKeyFrame(_readOnly_);}'}},
{ title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
{ title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), expertMode: false, toolType:DocumentType.RTF, funcs: { linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
- { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: { linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
+ { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), expertMode: false, toolType:DocumentType.INK, funcs: {hidden: `IsExploreMode()`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available
{ title: "Doc", icon: "Doc", toolTip: "Freeform Doc tools", subMenu: CurrentUserUtils.freeTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
{ title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
{ title: "Stack", icon: "View", toolTip: "Stacking tools", subMenu: CurrentUserUtils.stackTools(), expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectionManager_selectedDocType(self.toolType, self.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(self.toolType, self.expertMode)`} }, // Always available
@@ -892,7 +891,6 @@ export class CurrentUserUtils {
doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false);
doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
doc.userBackgroundColor ?? (doc.userBackgroundColor = Colors.DARK_GRAY);
- addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${doc.userBackgroundColor} !important` });
doc.userVariantColor ?? (doc.userVariantColor = Colors.MEDIUM_BLUE);
doc.userColor ?? (doc.userColor = Colors.LIGHT_GRAY);
doc.userTheme ?? (doc.userTheme = ColorScheme.Dark);
@@ -920,10 +918,6 @@ export class CurrentUserUtils {
Doc.GetProto(DocCast(Doc.UserDoc().emptyWebpage)).data = new WebField("https://www.wikipedia.org")
- if (doc.activeDashboard instanceof Doc) {
- // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
- doc.activeDashboard.colorScheme = doc.activeDashboard.colorScheme === ColorScheme.Light ? undefined : doc.activeDashboard.colorScheme;
- }
new LinkManager();
DocServer.CacheNeedsUpdate && setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
@@ -1028,6 +1022,7 @@ export class CurrentUserUtils {
}
ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
+ScriptingGlobals.add(function IsExploreMode() { return DocumentView.ExploreMode; }, "is Dash in exploration mode");
ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 6d6eaebec..f86f9a3e5 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -14,7 +14,7 @@ import { SelectionManager } from './SelectionManager';
import { SnappingManager } from './SnappingManager';
import { UndoManager } from './UndoManager';
-export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
+export type dropActionType = 'embed' | 'copy' | 'move' | 'add' | 'same' | 'inSame' | 'proto' | 'none' | undefined; // undefined = move, "same" = move but don't call dropPropertiesToRemove
/**
* Initialize drag
@@ -324,7 +324,7 @@ export namespace DragManager {
export let CanEmbed = false;
export let DocDragData: DocumentDragData | undefined;
export function StartDrag(eles: HTMLElement[], dragData: { [id: string]: any }, downX: number, downY: number, options?: DragOptions, finishDrag?: (dropData: DragCompleteEvent) => void, dragUndoName?: string) {
- if (dragData.dropAction === 'none') return;
+ if (dragData.dropAction === 'none' || DocumentView.ExploreMode) return;
DocDragData = dragData as DocumentDragData;
const batch = UndoManager.StartBatch(dragUndoName ?? 'document drag');
eles = eles.filter(e => e);
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
index 2fc811b09..146eed6c2 100644
--- a/src/client/util/LinkFollower.ts
+++ b/src/client/util/LinkFollower.ts
@@ -68,22 +68,23 @@ export class LinkFollower {
? linkDoc.link_anchor_2
: linkDoc.link_anchor_1
) as Doc;
+ const srcAnchor = LinkManager.getOppositeAnchor(linkDoc, target) ?? sourceDoc;
if (target) {
const doFollow = (canToggle?: boolean) => {
const toggleTarget = canToggle && BoolCast(sourceDoc.followLinkToggle);
const options: DocFocusOptions = {
- playAudio: BoolCast(sourceDoc.followLinkAudio),
- playMedia: BoolCast(sourceDoc.followLinkVideo),
+ playAudio: BoolCast(srcAnchor.followLinkAudio),
+ playMedia: BoolCast(srcAnchor.followLinkVideo),
toggleTarget,
noSelect: true,
willPan: true,
- willZoomCentered: BoolCast(sourceDoc.followLinkZoom, false),
- zoomTime: NumCast(sourceDoc.followLinkTransitionTime, 500),
- zoomScale: Cast(sourceDoc.followLinkZoomScale, 'number', null),
- easeFunc: StrCast(sourceDoc.followLinkEase, 'ease') as any,
- openLocation: StrCast(sourceDoc.followLinkLocation, OpenWhere.lightbox) as OpenWhere,
- effect: sourceDoc,
- zoomTextSelections: BoolCast(sourceDoc.followLinkZoomText),
+ willZoomCentered: BoolCast(srcAnchor.followLinkZoom, false),
+ zoomTime: NumCast(srcAnchor.followLinkTransitionTime, 500),
+ zoomScale: Cast(srcAnchor.followLinkZoomScale, 'number', null),
+ easeFunc: StrCast(srcAnchor.followLinkEase, 'ease') as any,
+ openLocation: StrCast(srcAnchor.followLinkLocation, OpenWhere.lightbox) as OpenWhere,
+ effect: srcAnchor,
+ zoomTextSelections: BoolCast(srcAnchor.followLinkZoomText),
};
if (target.type === DocumentType.PRES) {
const containerDocContext = DocumentManager.GetContextPath(sourceDoc, true); // gather all views that affect layout of sourceDoc so we can revert them after playing the rail
@@ -97,7 +98,7 @@ export class LinkFollower {
}
};
let movedTarget = false;
- if (sourceDoc.followLinkLocation === OpenWhere.inParent) {
+ if (srcAnchor.followLinkLocation === OpenWhere.inParent) {
const sourceDocParent = DocCast(sourceDoc.embedContainer);
if (target.embedContainer instanceof Doc && target.embedContainer !== sourceDocParent) {
Doc.RemoveDocFromList(target.embedContainer, Doc.LayoutFieldKey(target.embedContainer), target);
@@ -109,11 +110,11 @@ export class LinkFollower {
}
Doc.SetContainer(target, sourceDocParent);
const moveTo = [NumCast(sourceDoc.x) + NumCast(sourceDoc.followLinkXoffset), NumCast(sourceDoc.y) + NumCast(sourceDoc.followLinkYoffset)];
- if (sourceDoc.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
+ if (srcAnchor.followLinkXoffset !== undefined && moveTo[0] !== target.x) {
target.x = moveTo[0];
movedTarget = true;
}
- if (sourceDoc.followLinkYoffset !== undefined && moveTo[1] !== target.y) {
+ if (srcAnchor.followLinkYoffset !== undefined && moveTo[1] !== target.y) {
target.y = moveTo[1];
movedTarget = true;
}
diff --git a/src/client/util/ServerStats.tsx b/src/client/util/ServerStats.tsx
index ac9fecd5c..08dbaac5d 100644
--- a/src/client/util/ServerStats.tsx
+++ b/src/client/util/ServerStats.tsx
@@ -46,12 +46,12 @@ export class ServerStats extends React.Component<{}> {
<div
style={{
display: 'flex',
- height: 300,
+ height: '100%',
width: 400,
background: SettingsManager.userBackgroundColor,
opacity: 0.6,
}}>
- <div style={{ width: 300, height: 100, margin: 'auto', display: 'flex', flexDirection: 'column' }}>
+ <div style={{ width: 300, margin: 'auto', display: 'flex', flexDirection: 'column' }}>
{PingManager.Instance.IsBeating ? 'The server connection is active' : 'The server connection has been interrupted.NOTE: Any changes made will appear to persist but will be lost after a browser refreshes.'}
<br />
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 085ff4bb1..dc852596f 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -20,10 +20,9 @@ import { undoBatch } from './UndoManager';
export enum ColorScheme {
Dark = 'Dark',
Light = 'Light',
- CoolBlue = 'Cool Blue',
- Cupcake = 'Cupcake',
- System = 'Match System',
Custom = 'Custom',
+ CoolBlue = 'CoolBlue',
+ Cupcake = 'Cupcake',
}
export enum freeformScrollMode {
@@ -50,7 +49,21 @@ export class SettingsManager extends React.Component<{}> {
constructor(props: {}) {
super(props);
SettingsManager.Instance = this;
+ this.matchSystem();
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
+ if (Doc.UserDoc().userThemeSystem) {
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) this.changeColorScheme(ColorScheme.Dark);
+ if (window.matchMedia('(prefers-color-scheme: light)').matches) this.changeColorScheme(ColorScheme.Light);
+ }
+ // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+ });
}
+ matchSystem = () => {
+ if (Doc.UserDoc().userThemeSystem) {
+ if (window.matchMedia('(prefers-color-scheme: dark)').matches) this.changeColorScheme(ColorScheme.Dark);
+ if (window.matchMedia('(prefers-color-scheme: light)').matches) this.changeColorScheme(ColorScheme.Light);
+ }
+ };
public close = action(() => (this.isOpen = false));
public open = action(() => (this.isOpen = true));
@@ -88,6 +101,10 @@ export class SettingsManager extends React.Component<{}> {
});
@undoBatch switchUserColor = action((color: string) => (Doc.UserDoc().userColor = color));
@undoBatch switchUserVariantColor = action((color: string) => (Doc.UserDoc().userVariantColor = color));
+ @undoBatch userThemeSystemToggle = action(() => {
+ Doc.UserDoc().userThemeSystem = !Doc.UserDoc().userThemeSystem;
+ this.matchSystem();
+ });
@undoBatch playgroundModeToggle = action(() => {
this.playgroundMode = !this.playgroundMode;
if (this.playgroundMode) {
@@ -123,18 +140,11 @@ export class SettingsManager extends React.Component<{}> {
break;
case ColorScheme.Custom:
break;
- case ColorScheme.System:
- default:
- window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
- e.matches ? ColorScheme.Dark : ColorScheme.Light; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
- });
- break;
}
});
@computed get colorsContent() {
- const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.Cupcake, ColorScheme.CoolBlue, ColorScheme.Custom, ColorScheme.System];
- const schemeMap = ['Light', 'Dark', 'Cupcake', 'Cool Blue', 'Custom', 'Match System'];
+ const schemeMap = Array.from(Object.keys(ColorScheme));
const userTheme = StrCast(Doc.UserDoc().userTheme);
return (
<div style={{ width: '100%' }}>
@@ -144,15 +154,20 @@ export class SettingsManager extends React.Component<{}> {
type={Type.TERT}
closeOnSelect={false}
selectedVal={userTheme}
- setSelectedVal={scheme => this.changeColorScheme(scheme as string)}
- items={colorSchemes.map((scheme, i) => ({
- text: schemeMap[i],
+ setSelectedVal={scheme => {
+ this.changeColorScheme(scheme as string);
+ Doc.UserDoc().userThemeSystem = false;
+ }}
+ items={Object.keys(ColorScheme).map((scheme, i) => ({
+ text: schemeMap[i].replace(/([a-z])([A-Z])/, '$1 $2'),
val: scheme,
}))}
dropdownType={DropdownType.SELECT}
color={SettingsManager.userColor}
fillWidth
/>
+ <Toggle formLabel="Match System" toggleType={ToggleType.SWITCH} color={SettingsManager.userColor} toggleStatus={BoolCast(Doc.UserDoc().userThemeSystem)} onClick={this.userThemeSystemToggle} />
+
{userTheme === ColorScheme.Custom && (
<Group formLabel="Custom Theme">
<ColorPicker
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 7e55b0ebc..40eb1fe2b 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -90,7 +90,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
@computed
get Bounds() {
- if (LinkFollower.IsFollowing) return { x: 0, y: 0, r: 0, b: 0 };
+ if (LinkFollower.IsFollowing || DocumentView.ExploreMode) return { x: 0, y: 0, r: 0, b: 0 };
const views = SelectionManager.Views();
return views
.filter(dv => dv.props.renderDepth > 0)
@@ -262,6 +262,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
}
});
if (!this._iconifyBatch) {
+ (document.activeElement as any).blur?.();
this._iconifyBatch = UndoManager.StartBatch(forceDeleteOrIconify ? 'delete selected docs' : 'iconifying');
} else {
forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
@@ -804,8 +805,6 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
</Tooltip>
);
- const colorScheme = StrCast(Doc.ActiveDashboard?.colorScheme);
-
const leftBounds = this.props.boundsLeft;
const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
@@ -818,7 +817,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const useRotation = !hideResizers && seldocview.rootDoc.type !== DocumentType.EQUATION && seldocview.props.CollectionFreeFormDocumentView; // when do we want an object to not rotate?
const rotation = SelectionManager.Views().length == 1 ? NumCast(seldocview.rootDoc._rotation) : 0;
- const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
+ const resizerScheme = '';
// Radius constants
const useRounding = seldocview.ComponentView instanceof ImageBox || seldocview.ComponentView instanceof FormattedTextBox || seldocview.ComponentView instanceof CollectionFreeFormView;
@@ -853,7 +852,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
const titleArea = this._editingTitle ? (
<input
ref={this._keyinput}
- className={`documentDecorations-title${colorScheme}`}
+ className="documentDecorations-title"
type="text"
name="dynbox"
autoComplete="on"
@@ -871,7 +870,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
e.stopPropagation;
}}>
{hideTitle ? null : (
- <span className={`documentDecorations-titleSpan${colorScheme}`} onPointerDown={this.onTitleDown}>
+ <span className="documentDecorations-titleSpan" onPointerDown={this.onTitleDown}>
{this.selectionTitle}
</span>
)}
@@ -887,7 +886,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
);
return (
- <div className={`documentDecorations${colorScheme}`} style={{ display: this._showNothing ? 'none' : undefined }}>
+ <div className="documentDecorations" style={{ display: this._showNothing ? 'none' : undefined }}>
<div
className="documentDecorations-background"
style={{
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index f5ffff639..f63a27426 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -82,9 +82,8 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
screenToLocal = () => this.props.ScreenToLocalTransform().scale(this.props.NativeDimScaling?.() || 1);
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
- if (this._subContentView) {
- return this._subContentView.getAnchor?.(addAsAnnotation) || this.rootDoc;
- }
+ const subAnchor = this._subContentView?.getAnchor?.(addAsAnnotation);
+ if (subAnchor !== this.rootDoc && subAnchor) return subAnchor;
if (!addAsAnnotation && !pinProps) return this.rootDoc;
diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss
index f86a1d211..9a9b8a437 100644
--- a/src/client/views/LightboxView.scss
+++ b/src/client/views/LightboxView.scss
@@ -5,7 +5,6 @@
top: 10;
background: transparent;
border-radius: 8;
- color: white;
opacity: 0.7;
width: 25;
flex-direction: column;
@@ -17,11 +16,10 @@
.lightboxView-tabBtn {
margin: auto;
position: absolute;
- right: 38;
+ right: 45;
top: 10;
background: transparent;
border-radius: 8;
- color: white;
opacity: 0.7;
width: 25;
flex-direction: column;
@@ -33,11 +31,10 @@
.lightboxView-penBtn {
margin: auto;
position: absolute;
- right: 70;
+ right: 80;
top: 10;
background: transparent;
border-radius: 8;
- color: white;
opacity: 0.7;
width: 25;
flex-direction: column;
@@ -49,11 +46,10 @@
.lightboxView-exploreBtn {
margin: auto;
position: absolute;
- right: 100;
+ right: 115;
top: 10;
background: transparent;
border-radius: 8;
- color: white;
opacity: 0.7;
width: 25;
flex-direction: column;
@@ -68,7 +64,6 @@
left: 0;
width: 100%;
height: 100%;
- background: #000000bb;
z-index: 1000;
.lightboxView-contents {
position: absolute;
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 8f081b321..93eaec959 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,15 +1,17 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Toggle, ToggleType, Type } from 'browndash-components';
import { action, computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../fields/Doc';
import { InkTool } from '../../fields/InkField';
-import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../Utils';
import { DocUtils } from '../documents/Documents';
import { DocumentManager } from '../util/DocumentManager';
import { LinkManager } from '../util/LinkManager';
import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
import { Transform } from '../util/Transform';
import { CollectionDockingView } from './collections/CollectionDockingView';
import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
@@ -118,7 +120,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
bottom,
}}>
- <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
+ <div className="lightboxView-navBtn" title={color} style={{ top, color: SettingsManager.userColor, background: undefined }} onClick={click}>
<div style={{ height: 10 }}>{color}</div>
<FontAwesomeIcon icon={icon as any} size="3x" />
</div>
@@ -229,6 +231,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
downx = e.clientX;
downy = e.clientY;
}}
+ style={{ background: SettingsManager.userBackgroundColor }}
onClick={e => {
if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
LightboxView.SetLightboxDoc(undefined);
@@ -242,6 +245,8 @@ export class LightboxView extends React.Component<LightboxViewProps> {
width: this.lightboxWidth(),
height: this.lightboxHeight(),
clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
+ background: SettingsManager.userBackgroundColor,
+ color: SettingsManager.userColor,
}}>
{/* <CollectionMenu /> TODO:glr This is where it would go*/}
@@ -299,47 +304,68 @@ export class LightboxView extends React.Component<LightboxViewProps> {
this.future()?.length.toString()
)}
<LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} />
- <div
- className="lightboxView-navBtn"
- title="toggle fit width"
- onClick={e => {
- e.stopPropagation();
- LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
- }}>
- <FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
+ <div className="lightboxView-navBtn">
+ <Toggle
+ tooltip="toggle reading view"
+ color={SettingsManager.userColor}
+ background={BoolCast(LightboxView.LightboxDoc!._layout_fitWidth) ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+ toggleType={ToggleType.BUTTON}
+ type={Type.TERT}
+ toggleStatus={BoolCast(LightboxView.LightboxDoc!._layout_fitWidth)}
+ onClick={e => {
+ e.stopPropagation();
+ LightboxView.LightboxDoc!._layout_fitWidth = !LightboxView.LightboxDoc!._layout_fitWidth;
+ }}
+ icon={<FontAwesomeIcon icon={LightboxView.LightboxDoc?._layout_fitWidth ? 'book-open' : 'book'} size="sm" />}
+ />
</div>
- <div
- className="lightboxView-tabBtn"
- title="open in tab"
- onClick={e => {
- const lightdoc = LightboxView._docTarget || LightboxView._doc!;
- e.stopPropagation();
- Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
- CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
- SelectionManager.DeselectAll();
- LightboxView.SetLightboxDoc(undefined);
- }}>
- <FontAwesomeIcon icon={'file-download'} size="2x" />
+ <div className="lightboxView-tabBtn">
+ <Toggle
+ tooltip="open document in a tab"
+ color={SettingsManager.userColor}
+ background={SettingsManager.userBackgroundColor}
+ toggleType={ToggleType.BUTTON}
+ type={Type.TERT}
+ icon={<FontAwesomeIcon icon="file-download" size="sm" />}
+ onClick={e => {
+ const lightdoc = LightboxView._docTarget || LightboxView._doc!;
+ e.stopPropagation();
+ Doc.RemoveDocFromList(Doc.MyRecentlyClosed, 'data', lightdoc);
+ CollectionDockingView.AddSplit(lightdoc, OpenWhereMod.none);
+ SelectionManager.DeselectAll();
+ LightboxView.SetLightboxDoc(undefined);
+ }}
+ />
</div>
- <div
- className="lightboxView-penBtn"
- title="toggle pen annotation"
- style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }}
- onClick={e => {
- e.stopPropagation();
- Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
- }}>
- <FontAwesomeIcon color={Doc.ActiveTool === InkTool.Pen ? 'black' : 'white'} icon={'pen'} size="2x" />
+ <div className="lightboxView-penBtn">
+ <Toggle
+ tooltip="toggle pen annotation"
+ color={SettingsManager.userColor}
+ background={Doc.ActiveTool === InkTool.Pen ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+ toggleType={ToggleType.BUTTON}
+ toggleStatus={Doc.ActiveTool === InkTool.Pen}
+ type={Type.TERT}
+ icon={<FontAwesomeIcon icon="pen" size="sm" />}
+ onClick={e => {
+ e.stopPropagation();
+ Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+ }}
+ />
</div>
- <div
- className="lightboxView-exploreBtn"
- title="toggle explore mode to navigate among documents only"
- style={{ background: DocumentView.ExploreMode ? 'white' : undefined }}
- onClick={action(e => {
- e.stopPropagation();
- DocumentView.ExploreMode = !DocumentView.ExploreMode;
- })}>
- <FontAwesomeIcon color={DocumentView.ExploreMode ? 'black' : 'white'} icon={'globe-americas'} size="2x" />
+ <div className="lightboxView-exploreBtn">
+ <Toggle
+ tooltip="toggle explore mode to navigate among documents only"
+ color={SettingsManager.userColor}
+ background={DocumentView.ExploreMode ? SettingsManager.userVariantColor : SettingsManager.userBackgroundColor}
+ toggleType={ToggleType.BUTTON}
+ type={Type.TERT}
+ toggleStatus={DocumentView.ExploreMode}
+ icon={<FontAwesomeIcon icon="globe-americas" size="sm" />}
+ onClick={action(e => {
+ e.stopPropagation();
+ DocumentView.ExploreMode = !DocumentView.ExploreMode;
+ })}
+ />
</div>
</div>
);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index c880dde9a..4fb2ac279 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -62,8 +62,7 @@ h1,
pointer-events: none;
}
-.mainView-container,
-.mainView-container-Dark {
+.mainView-container {
width: 100%;
height: 100%;
position: absolute;
@@ -74,58 +73,12 @@ h1,
touch-action: none;
}
-.mainView-container,
-.mainView-container-Dark {
- .lm_header .lm_tab {
- padding: 0px;
- //opacity: 0.7;
- box-shadow: none;
- height: 25px;
- border-bottom: black solid;
- }
-}
-
.mainView-container {
color: $dark-gray;
.lm_goldenlayout {
background: $medium-gray;
}
-
- .lm_title {
- background: $light-gray;
- color: $dark-gray;
- }
-}
-
-.mainView-container-Dark {
- color: $light-gray;
-
- .lm_goldenlayout {
- background: $medium-gray;
- }
-
- .lm_title {
- background: $dark-gray;
- color: unset;
- }
-
- .marquee {
- border-color: $white;
- }
-
- #search-input {
- background: $light-gray;
- }
-
- .contextMenu-cont,
- .contextMenu-item {
- background: $dark-gray;
- }
-
- .contextMenu-item:hover {
- background: $medium-gray;
- }
}
.mainView-dashboardArea {
@@ -143,6 +96,7 @@ h1,
top: 0;
}
+.mainView-propertiesDragger-minified,
.mainView-propertiesDragger {
//background-color: rgb(140, 139, 139);
background-color: $light-gray;
@@ -155,9 +109,9 @@ h1,
border-bottom-left-radius: 10px;
border-right: unset;
z-index: 41; // lm_maximised has a z-index of 40 and this needs to be above that
- display: flex;
align-items: center;
padding: 4px;
+ display: flex;
.mainView-propertiesDragger-icon {
width: 10px;
@@ -171,9 +125,11 @@ h1,
cursor: grab;
}
}
+.mainView-propertiesDragger-minified {
+ width: 10px;
+}
-.mainView-innerContent,
-.mainView-innerContent-Dark {
+.mainView-innerContent {
display: contents;
flex-direction: row;
position: relative;
@@ -206,44 +162,6 @@ h1,
background-color: $light-gray;
}
-.mainView-innerContent-Dark {
- .propertiesView {
- background-color: #252525;
-
- input {
- background-color: $medium-gray;
- }
-
- .propertiesView-sharingTable {
- background-color: $medium-gray;
- }
-
- .editable-title {
- background-color: $medium-gray;
- }
-
- .propertiesView-field {
- background-color: $medium-gray;
- }
- }
-
- .mainView-propertiesDragger,
- .mainView-libraryHandle {
- background: #353535;
- }
-}
-
-.mainView-container-Dark {
- .contextMenu-cont {
- background: $medium-gray;
- color: $white;
-
- input::placeholder {
- color: $white;
- }
- }
-}
-
.mainView-leftMenuPanel {
min-width: var(--menuPanelWidth);
border-right: $standard-border;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 707cf3a34..d6f5d63fe 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -112,9 +112,6 @@ export class MainView extends React.Component {
@computed private get userDoc() {
return Doc.UserDoc();
}
- @computed private get colorScheme() {
- return StrCast(Doc.ActiveDashboard?.colorScheme);
- }
@observable mainDoc: Opt<Doc>;
@computed private get mainContainer() {
if (window.location.pathname.startsWith('/doc/') && Doc.CurrentUserEmail === 'guest') {
@@ -800,7 +797,7 @@ export class MainView extends React.Component {
return (
<>
{this._hideUI ? null : this.leftMenuPanel}
- <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
+ <div key="inner" className="mainView-innerContent">
{this.flyout}
<div
className="mainView-libraryHandle"
@@ -812,7 +809,11 @@ export class MainView extends React.Component {
{this.dockingContent}
{this._hideUI ? null : (
- <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ background: SettingsManager.userVariantColor, right: this.propertiesWidth() - 1 }}>
+ <div
+ className={`mainView-propertiesDragger${this.propertiesWidth() < 10 ? '-minified' : ''}`}
+ key="props"
+ onPointerDown={this.onPropertiesPointerDown}
+ style={{ background: SettingsManager.userVariantColor, right: this.propertiesWidth() - 1 }}>
<FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={SettingsManager.userColor} size="sm" />
</div>
)}
@@ -843,7 +844,7 @@ export class MainView extends React.Component {
).observe(r);
}}
style={{
- color: this.colorScheme === ColorScheme.Dark ? 'rgb(205,205,205)' : 'black',
+ color: 'black',
height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
width: '100%',
}}>
@@ -1003,7 +1004,7 @@ export class MainView extends React.Component {
render() {
return (
<div
- className={`mainView-container ${this.colorScheme}`}
+ className="mainView-container"
style={{
color: SettingsManager.userColor,
background: SettingsManager.userBackgroundColor,
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 3d8d569fa..a958607de 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -10,7 +10,7 @@ import { unimplementedFunction, Utils } from '../../Utils';
import { Docs, DocUtils } from '../documents/Documents';
import { DragManager } from '../util/DragManager';
import { FollowLinkScript } from '../util/LinkFollower';
-import { undoBatch, UndoManager } from '../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../util/UndoManager';
import './MarqueeAnnotator.scss';
import { DocumentView } from './nodes/DocumentView';
import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
@@ -55,7 +55,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
UndoManager.RunInBatch(() => this.props.anchorMenuCrop?.(this.highlight('', true, undefined, false), true), 'cropping');
}
};
- AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true));
+ AnchorMenu.Instance.OnClick = undoable((e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
AnchorMenu.Instance.OnAudio = unimplementedFunction;
AnchorMenu.Instance.Highlight = this.highlight;
AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 1d88e9ad6..e3a43d45f 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -2,7 +2,6 @@ import { action, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Opt } from '../../fields/Doc';
-import { StrCast } from '../../fields/Types';
import { lightOrDark, returnFalse } from '../../Utils';
import { Docs, DocumentOptions, DocUtils } from '../documents/Documents';
import { ImageUtils } from '../util/Import & Export/ImageUtils';
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 40d42a4de..d1561fd67 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -57,6 +57,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
return !targetDoc ? null : (
<Toggle
toggleStatus={BoolCast(targetDoc[property])}
+ tooltip={tooltip(BoolCast(targetDoc[property]))}
text={label(targetDoc?.[property])}
color={SettingsManager.userColor}
icon={icon(targetDoc?.[property] as any)}
@@ -165,9 +166,9 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed get forceActiveButton() {
//select text
return this.propertyToggleBtn(
- on => (on ? 'INACTIVE INTERACTION' : 'ACTIVE INTERACTION'),
+ on => (on ? 'SELECT TO INTERACT' : 'ALWAYS INTERACTIVE'),
'_forceActive',
- on => `${on ? 'Select to activate' : 'Contents always active'} `,
+ on => `${on ? 'Document must be selected to interact with its contents' : 'Contents always active (respond to click/drag events)'} `,
on => <MdTouchApp /> // 'eye'
);
}
@@ -210,9 +211,12 @@ export class PropertiesButtons extends React.Component<{}, {}> {
@computed get layout_fitWidthButton() {
return this.propertyToggleBtn(
- on => (on ? 'RESTRICT WIDTH' : 'FIT WIDTH'), //'Fit\xA0Width',
+ on => (on ? 'SCALED VIEW' : 'READING VIEW'), //'Fit\xA0Width',
'_layout_fitWidth',
- on => `${on ? "Don't" : 'Do'} fit content to width of container`,
+ on =>
+ on
+ ? "Scale document so it's width and height fit container (no effect when document is viewed on freeform canvas)"
+ : "Scale document so it's width fits container and its height expands/contracts to fit available space (no effect when document is viewed on freeform canvas)",
on => (on ? <AiOutlineColumnWidth /> : <RxWidth />) // 'arrows-alt-h'
);
}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 1a4b8d7d2..d7537bffb 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -52,10 +52,6 @@ export enum StyleProp {
Highlighting = 'highlighting', // border highlighting
}
-function darkScheme() {
- return Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark;
-}
-
function toggleLockedPosition(doc: Doc) {
UndoManager.RunInBatch(
() =>
@@ -135,7 +131,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
}
return undefined;
case StyleProp.DocContents:return undefined;
- case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? 'lightgrey' : 'dimgrey';
+ case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : 'dimgrey';
case StyleProp.Opacity: return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null));
case StyleProp.HideLinkBtn:return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton);
case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._text_fontSize, StrCast(Doc.UserDoc().fontSize)));
@@ -203,21 +199,21 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
// prettier-ignore
switch (doc?.type) {
case DocumentType.SLIDER: break;
- case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? '' : ''); break;
- case DocumentType.PRES: docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); break;
+ case DocumentType.PRESELEMENT: docColor = docColor || ""; break;
+ case DocumentType.PRES: docColor = docColor || 'transparent'; break;
case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break;
- case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+ case DocumentType.RTF: docColor = docColor || Colors.LIGHT_GRAY; break;
case DocumentType.INK: docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break;
case DocumentType.EQUATION: docColor = docColor || 'transparent'; break;
- case DocumentType.LABEL: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
- case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+ case DocumentType.LABEL: docColor = docColor || Colors.LIGHT_GRAY; break;
+ case DocumentType.BUTTON: docColor = docColor || Colors.LIGHT_GRAY; break;
case DocumentType.LINK: docColor = (isAnchor ? docColor : '') || 'transparent'; break;
case DocumentType.IMG:
case DocumentType.WEB:
case DocumentType.PDF:
case DocumentType.MAP:
case DocumentType.SCREENSHOT:
- case DocumentType.VID: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break;
+ case DocumentType.VID: docColor = docColor || (Colors.LIGHT_GRAY); break;
case DocumentType.COL:
if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
docColor = docColor || (Doc.IsSystem(doc)
@@ -227,11 +223,11 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
: doc?._isGroup
? undefined
: doc._type_collection === CollectionViewType.Stacking ?
- (darkScheme() ? Colors.MEDIUM_GRAY : Colors.DARK_GRAY)
- : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (darkScheme() ? Colors.BLACK : Colors.MEDIUM_GRAY));
+ (Colors.DARK_GRAY)
+ : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (Colors.MEDIUM_GRAY));
break;
//if (doc._type_collection !== CollectionViewType.Freeform && doc._type_collection !== CollectionViewType.Time) return "rgb(62,62,62)";
- default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE);
+ default: docColor = docColor || (Colors.WHITE);
}
return (docColor && !doc) ? DashColor(docColor).fade(0.5).toString() : docColor;
}
@@ -247,7 +243,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
? '4px 4px 10px 2px'
: lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString
? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide)
- : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
+ : `${Colors.MEDIUM_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}`
);
case DocumentType.LABEL:
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index 6bd1d9f5f..8319f19ca 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -74,7 +74,10 @@
.carousel3DView-fwd,
.carousel3DView-back {
- top: 50%;
+ top: 0;
+ background: transparent;
+ width: calc((1 - #{$CAROUSEL3D_CENTER_SCALE} * 0.33) / 2 * 100%);
+ height: 100%;
}
.carousel3DView-fwd-scroll,
@@ -108,8 +111,6 @@
opacity: 1;
}
-.carousel3DView-back:hover,
-.carousel3DView-fwd:hover,
.carousel3DView-back-scroll:hover,
.carousel3DView-fwd-scroll:hover {
background: lightgray;
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index d94e552b4..4bc72772a 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -2,14 +2,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { returnFalse, returnZero, Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
import { StyleProp } from '../StyleProvider';
import './CollectionCarousel3DView.scss';
import { CollectionSubView } from './CollectionSubView';
@@ -46,6 +47,15 @@ export class CollectionCarousel3DView extends CollectionSubView() {
.translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this.props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
.scale(1 / this.centerScale);
+ focus = (anchor: Doc, options: DocFocusOptions) => {
+ const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+ if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
+ options.didMove = true;
+ const target = DocCast(anchor.annotationOn) ?? anchor;
+ const index = docs.indexOf(target);
+ index !== -1 && (this.layoutDoc._carousel_index = index);
+ return undefined;
+ };
@computed get content() {
const currentIndex = NumCast(this.layoutDoc._carousel_index);
const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
@@ -61,6 +71,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
LayoutTemplateString={this.props.childLayoutString}
Document={childPair.layout}
DataDoc={childPair.data}
+ focus={this.focus}
ScreenToLocalTransform={this.childScreenToLocal}
isContentActive={this.isChildContentActive}
isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
@@ -85,8 +96,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.childLayoutPairs.length) % this.childLayoutPairs.length;
};
- onArrowClick = (e: React.MouseEvent, direction: number) => {
- e.stopPropagation();
+ onArrowClick = (direction: number) => {
this.changeSlide(direction);
!this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = direction === 1 ? 'fwd' : 'back'); // while autoscroll is on, keep the other autoscroll button hidden
!this.layoutDoc.autoScrollOn && this.fadeScrollButton(); // keep pause button visible while autoscroll is on
@@ -117,16 +127,11 @@ export class CollectionCarousel3DView extends CollectionSubView() {
};
@computed get buttons() {
- if (!this.props.isContentActive()) return null;
return (
<div className="arrow-buttons">
- <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={e => this.onArrowClick(e, -1)}>
- <FontAwesomeIcon icon="angle-left" size={'2x'} />
- </div>
- <div key="fwd" className="carousel3DView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={e => this.onArrowClick(e, 1)}>
- <FontAwesomeIcon icon="angle-right" size={'2x'} />
- </div>
- {this.autoScrollButton}
+ <div title="click to go back" key="back" className="carousel3DView-back" onClick={e => this.onArrowClick(-1)} />
+ <div title="click to advance" key="fwd" className="carousel3DView-fwd" onClick={e => this.onArrowClick(1)} />
+ {/* {this.autoScrollButton} */}
</div>
);
}
@@ -165,7 +170,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
<div className="carousel-wrapper" style={{ transform: `translateX(${this.translateX}px)` }}>
{this.content}
</div>
- {this.props.Document._chromeHidden ? null : this.buttons}
+ {this.buttons}
<div className="dot-bar">{this.dots}</div>
</div>
);
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 8660113cd..130b31325 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,8 +1,7 @@
-
.collectionCarouselView-outer {
- height : 100%;
+ height: 100%;
.collectionCarouselView-caption {
- height: 50;
+ height: 50;
display: inline-block;
width: 100%;
}
@@ -13,7 +12,8 @@
user-select: none;
}
}
-.carouselView-back, .carouselView-fwd {
+.carouselView-back,
+.carouselView-fwd {
position: absolute;
display: flex;
top: 42.5%;
@@ -22,18 +22,19 @@
align-items: center;
border-radius: 5px;
justify-content: center;
- color: rgba(255,255,255,0.5);
- background : rgba(0,0,0, 0.1);
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
&:hover {
- color:white;
+ color: white;
}
}
.carouselView-fwd {
- right: 0;
+ right: 20;
}
.carouselView-back {
- left: 0;
+ left: 20;
}
-.carouselView-back:hover, .carouselView-fwd:hover {
+.carouselView-back:hover,
+.carouselView-fwd:hover {
background: lightgray;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 7fa36d228..040a584b8 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Opt } from '../../../fields/Doc';
import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, StopEvent } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, StopEvent } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -44,12 +44,14 @@ export class CollectionCarouselView extends CollectionSubView() {
panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
+ @computed get marginX() {
+ return NumCast(this.layoutDoc.caption_xMargin, 50);
+ }
+ captionWidth = () => this.props.PanelWidth() - 2 * this.marginX;
@computed get content() {
const index = NumCast(this.layoutDoc._carousel_index);
const curDoc = this.childLayoutPairs?.[index];
- const captionProps = { ...this.props, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
- const marginX = NumCast(this.layoutDoc['caption_xMargin']);
- const marginY = NumCast(this.layoutDoc['caption_yMargin']);
+ const captionProps = { ...this.props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
const show_captions = StrCast(this.layoutDoc._layout_showCaption);
return !(curDoc?.layout instanceof Doc) ? null : (
<>
@@ -80,9 +82,9 @@ export class CollectionCarouselView extends CollectionSubView() {
style={{
display: show_captions ? undefined : 'none',
borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
- marginRight: marginX,
- marginLeft: marginX,
- width: `calc(100% - ${marginX * 2}px)`,
+ marginRight: this.marginX,
+ marginLeft: this.marginX,
+ width: `calc(100% - ${this.marginX * 2}px)`,
}}>
<FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
</div>
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index b13753025..c0530ab81 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,5 +1,285 @@
@import '../global/globalCssVariables.scss';
-@import '../../../../node_modules/golden-layout/src/css/goldenlayout-base.css';
+
+.lm_root {
+ position: relative;
+}
+.lm_row > .lm_item {
+ float: left;
+}
+.lm_content {
+ overflow: hidden;
+ position: relative;
+}
+.lm_dragging,
+.lm_dragging * {
+ cursor: move !important;
+ user-select: none;
+}
+.lm_maximised {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 40;
+}
+.lm_maximise_placeholder {
+ display: none;
+}
+.lm_splitter {
+ position: relative;
+ z-index: 20;
+}
+.lm_splitter:hover,
+.lm_splitter.lm_dragging {
+ background: orange;
+}
+.lm_splitter.lm_vertical .lm_drag_handle {
+ width: 100%;
+ position: absolute;
+ cursor: ns-resize;
+}
+.lm_splitter.lm_horizontal {
+ float: left;
+ height: 100%;
+}
+.lm_splitter.lm_horizontal .lm_drag_handle {
+ height: 100%;
+ position: absolute;
+ cursor: ew-resize;
+}
+.lm_header {
+ overflow: visible;
+ position: relative;
+ z-index: 1;
+}
+// .lm_header [class^='lm_'] {
+// box-sizing: content-box !important;
+// }
+.lm_header .lm_controls {
+ position: absolute;
+ right: 3px;
+}
+.lm_header .lm_controls > li {
+ cursor: pointer;
+ float: left;
+ width: 18px;
+ height: 18px;
+ text-align: center;
+ top: 3px;
+}
+.lm_header ul {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+}
+.lm_header .lm_tabs {
+ position: absolute;
+}
+.lm_header .lm_tab {
+ cursor: pointer;
+ float: left;
+ height: 25px;
+ padding: 0 10px 5px;
+ padding-right: 25px;
+ position: relative;
+ box-shadow: unset !important;
+}
+.lm_header .lm_tab i {
+ width: 2px;
+ height: 19px;
+ position: absolute;
+}
+.lm_header .lm_tab i.lm_left {
+ top: 0;
+ left: -2px;
+}
+.lm_header .lm_tab i.lm_right {
+ top: 0;
+ right: -2px;
+}
+.lm_header .lm_tab .lm_title {
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.lm_header .lm_tab .lm_close_tab {
+ width: 14px;
+ height: 14px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ text-align: center;
+}
+.lm_stack.lm_left .lm_header,
+.lm_stack.lm_right .lm_header {
+ height: 100%;
+}
+.lm_dragProxy.lm_left .lm_header,
+.lm_dragProxy.lm_right .lm_header,
+.lm_stack.lm_left .lm_header,
+.lm_stack.lm_right .lm_header {
+ width: 20px;
+ float: left;
+ vertical-align: top;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabs,
+.lm_dragProxy.lm_right .lm_header .lm_tabs,
+.lm_stack.lm_left .lm_header .lm_tabs,
+.lm_stack.lm_right .lm_header .lm_tabs {
+ transform-origin: left top;
+ top: 0;
+ width: 1000px;
+}
+.lm_dragProxy.lm_left .lm_header .lm_controls,
+.lm_dragProxy.lm_right .lm_header .lm_controls,
+.lm_stack.lm_left .lm_header .lm_controls,
+.lm_stack.lm_right .lm_header .lm_controls {
+ bottom: 0;
+}
+.lm_dragProxy.lm_left .lm_items,
+.lm_dragProxy.lm_right .lm_items,
+.lm_stack.lm_left .lm_items,
+.lm_stack.lm_right .lm_items {
+ float: left;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabs,
+.lm_stack.lm_left .lm_header .lm_tabs {
+ transform: rotate(-90deg) scaleX(-1);
+ left: 0;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabs .lm_tab,
+.lm_stack.lm_left .lm_header .lm_tabs .lm_tab {
+ transform: scaleX(-1);
+ margin-top: 1px;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabdropdown_list,
+.lm_stack.lm_left .lm_header .lm_tabdropdown_list {
+ top: initial;
+ right: initial;
+ left: 20px;
+}
+.lm_dragProxy.lm_right .lm_content {
+ float: left;
+}
+.lm_dragProxy.lm_right .lm_header .lm_tabs,
+.lm_stack.lm_right .lm_header .lm_tabs {
+ transform: rotate(90deg) scaleX(1);
+ left: 100%;
+ margin-left: 0;
+}
+.lm_dragProxy.lm_right .lm_header .lm_controls,
+.lm_stack.lm_right .lm_header .lm_controls {
+ left: 3px;
+}
+.lm_dragProxy.lm_right .lm_header .lm_tabdropdown_list,
+.lm_stack.lm_right .lm_header .lm_tabdropdown_list {
+ top: initial;
+ right: 20px;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_tab,
+.lm_stack.lm_bottom .lm_header .lm_tab {
+ margin-top: 0;
+ border-top: none;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_controls,
+.lm_stack.lm_bottom .lm_header .lm_controls {
+ top: 3px;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_tabdropdown_list,
+.lm_stack.lm_bottom .lm_header .lm_tabdropdown_list {
+ top: initial;
+ bottom: 20px;
+}
+.lm_drop_tab_placeholder {
+ float: left;
+ width: 100px;
+ height: 10px;
+ visibility: hidden;
+}
+.lm_header .lm_controls .lm_tabdropdown:before {
+ content: '';
+ width: 0;
+ height: 0;
+ vertical-align: middle;
+ display: inline-block;
+ border-top: 5px dashed;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ color: white;
+}
+.lm_header .lm_tabdropdown_list {
+ position: absolute;
+ top: 20px;
+ right: 0;
+ z-index: 5;
+ overflow: hidden;
+}
+.lm_header .lm_tabdropdown_list .lm_tab {
+ clear: both;
+ padding-right: 10px;
+ margin: 0;
+}
+.lm_header .lm_tabdropdown_list .lm_tab .lm_title {
+ width: 100px;
+}
+.lm_header .lm_tabdropdown_list .lm_close_tab {
+ display: none !important;
+}
+.lm_dragProxy {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 30;
+}
+.lm_dragProxy .lm_header {
+ background: transparent;
+}
+.lm_dragProxy .lm_content {
+ border-top: none;
+ overflow: hidden;
+}
+.lm_dropTargetIndicator {
+ display: none;
+ position: absolute;
+ z-index: 20;
+}
+.lm_dropTargetIndicator .lm_inner {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ top: 0;
+ left: 0;
+}
+.lm_transition_indicator {
+ display: none;
+ width: 20px;
+ height: 20px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 20;
+}
+.lm_popin {
+ width: 20px;
+ height: 20px;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ z-index: 9999;
+}
+.lm_popin > * {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+.lm_popin > .lm_bg {
+ z-index: 10;
+}
+.lm_popin > .lm_icon {
+ z-index: 20;
+} /*# sourceMappingURL=goldenlayout-base.css.map */
+
@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css';
.lm_title {
@@ -35,6 +315,8 @@
width: max-content;
height: 100%;
display: flex;
+ max-width: 100;
+ text-overflow: ellipsis;
}
.lm_active {
@@ -46,18 +328,33 @@
// font-weight: 700;
}
+.lm_header .lm_tabs {
+ overflow-y: hidden;
+ width: 100%;
+}
+ul.lm_tabs::before {
+ content: ' ';
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ z-index: 1;
+ pointer-events: none;
+ border: solid 1px black;
+}
.lm_header .lm_tab {
// padding: 0px; // moved to MainView.scss, othwerise they get overridden by default stylings
// opacity: 0.7;
// box-shadow: none;
// height: 25px;
// border-bottom: black solid;
+ border-bottom: unset !important;
+ border-top-right-radius: 5px;
+ border-top-left-radius: 5px;
.collectionDockingView-gear {
display: none;
}
}
-
.lm_header .lm_tab.lm_active {
padding: 0;
opacity: 1;
@@ -65,7 +362,11 @@
box-shadow: none;
height: 27px;
margin-right: 2px;
- // border-bottom: unset;
+ z-index: 2 !important;
+ border-right: solid 2px;
+ border-left: solid 2px;
+ border-top: solid 2px;
+ border-color: black;
.collectionDockingView-gear {
display: inline-block;
@@ -123,20 +424,55 @@
}
.lm_close_tab {
+ display: inline-flex !important;
padding: 0;
+ opacity: 1 !important;
align-self: center;
margin-right: 5px;
- background-color: black;
border-radius: 3px;
- opacity: 1 !important;
width: 15px !important;
height: 15px !important;
position: relative !important;
- display: inline-flex !important;
align-items: center;
top: 0 !important;
right: unset !important;
left: 0 !important;
+ background-image: unset !important;
+ &::before {
+ content: '\a0x\a0';
+ color: rgb(50, 50, 50);
+ margin: auto;
+ position: relative;
+ top: -2px;
+ }
+ &:hover {
+ &::before {
+ background: gray;
+ color: white;
+ }
+ }
+}
+.lm_close {
+ background-image: unset !important;
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
+ &::before {
+ content: 'x';
+ margin: auto;
+ position: relative;
+ top: -2;
+ font-size: medium;
+ font-family: sans-serif;
+ }
+}
+
+.lm_iconWrap {
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
}
.lm_tab,
@@ -154,14 +490,6 @@
top: 0;
left: 0;
- // overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu)
- .collectionDockingView-gear {
- padding-left: 5px;
- height: 15px;
- width: 18px;
- margin: auto;
- }
-
.collectionDockingView-drag {
touch-action: none;
position: absolute;
@@ -180,7 +508,6 @@
display: flex;
align-content: center;
justify-content: center;
- background: $dark-gray;
}
.lm_controls > li {
@@ -190,14 +517,38 @@
}
.lm_controls .lm_popout {
- transform: rotate(45deg);
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAQUlEQVR4nHXOQQ4AMAgCQeT/f6aXpsGK3jSTuCVJAAr7iBdoAwCKd0nwfaAdHbYERw5b44+E8JoBjEYGMBq5gAYP3usUDu2IvoUAAAAASUVORK5CYII=);
+ background-image: unset;
+ left: -3;
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
+ }
+ li.lm_popout::before {
+ content: '+';
+ margin: auto;
+ font-size: x-large;
+ top: -6;
+ position: relative;
+ }
+ .lm_maximise {
+ background-image: unset !important;
+ &::before {
+ content: '\25A3';
+ margin: auto;
+ font-size: medium;
+ position: relative;
+ }
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
}
.lm_maximised .lm_controls .lm_maximise {
- opacity: 1;
- transform: scale(0.8);
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAKElEQVR4nGP8////fwYCgImQAgYGBgYWKM2IR81/okwajIpgvsMbVgAwgQYRVakEKQAAAABJRU5ErkJggg==) !important;
+ &::before {
+ content: '\25A2';
+ }
}
.flexlayout__layout {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 8472c59db..4873a61ff 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -10,7 +10,7 @@ import { List } from '../../../fields/List';
import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
-import { emptyFunction, incrementTitleCopy } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, incrementTitleCopy } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
@@ -31,6 +31,7 @@ import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { TabDocView } from './TabDocView';
import React = require('react');
+import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -60,7 +61,7 @@ export class CollectionDockingView extends CollectionSubView() {
return this._goldenLayout._maximisedItem !== null;
}
private _goldenLayout: any = null;
-
+ static _highlightStyleSheet: any = addStyleSheet();
constructor(props: SubCollectionViewProps) {
super(props);
if (this.props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
@@ -330,6 +331,16 @@ export class CollectionDockingView extends CollectionSubView() {
width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
{ fireImmediately: true }
);
+ reaction(
+ () => [SettingsManager.userBackgroundColor, SettingsManager.userBackgroundColor],
+ () => {
+ clearStyleSheetRules(CollectionDockingView._highlightStyleSheet);
+ addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { background: `${SettingsManager.userBackgroundColor} !important` });
+ addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { color: `${SettingsManager.userColor} !important` });
+ addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${SettingsManager.userBackgroundColor} !important` });
+ },
+ { fireImmediately: true }
+ );
}
};
@@ -505,6 +516,23 @@ export class CollectionDockingView extends CollectionSubView() {
}
});
+ let addNewDoc = action(() => {
+ const dashboard = Doc.ActiveDashboard;
+ if (dashboard) {
+ dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
+ const docToAdd = Docs.Create.FreeformDocument([], {
+ _width: this.props.PanelWidth(),
+ _height: this.props.PanelHeight(),
+ _layout_fitWidth: true,
+ _freeform_backgroundGrid: true,
+ title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
+ });
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
+ inheritParentAcls(this.dataDoc, docToAdd, false);
+ CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
+ }
+ });
+
stack.header?.controlsContainer
.find('.lm_close') //get the close icon
.off('click') //unbind the current click handler
@@ -524,31 +552,18 @@ export class CollectionDockingView extends CollectionSubView() {
})
);
+ stack.element.click((e: any) => {
+ if (stack.contentItems.length === 0 && Array.from(document.elementsFromPoint(e.originalEvent.x, e.originalEvent.y)).some(ele => ele?.className === 'empty-tabs-message')) {
+ addNewDoc();
+ }
+ });
stack.header?.controlsContainer
.find('.lm_maximise') //get the close icon
.click(() => setTimeout(this.stateChanged));
stack.header?.controlsContainer
.find('.lm_popout') //get the popout icon
.off('click') //unbind the current click handler
- .click(
- action(() => {
- // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size
- const dashboard = Doc.ActiveDashboard;
- if (dashboard) {
- dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
- const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(),
- _height: this.props.PanelHeight(),
- _layout_fitWidth: true,
- _freeform_backgroundGrid: true,
- title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
- });
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
- inheritParentAcls(this.dataDoc, docToAdd, false);
- CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
- }
- })
- );
+ .click(addNewDoc);
};
render() {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 54a60271a..26272d2ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -210,11 +210,12 @@ export function CollectionSubView<X>(moreProps?: X) {
const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
const someMoved = !dropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
- if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
+ if ((!dropAction || dropAction === 'inSame' || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
if (movedDocs.length) {
- const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+ const canAdd =
+ (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
added = canAdd || moved ? moved : undefined;
} else {
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index eed04b3ee..9e5ac77d9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -149,7 +149,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
if (isAlreadyInTree() !== sameTree) {
console.log('WHAAAT');
}
- dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+ dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree && dragData.dropAction !== 'inSame' ? 'same' : dragData.dropAction;
e.stopPropagation();
}
};
@@ -438,7 +438,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
render() {
TraceMobx();
- const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
+ const scale = this.props.NativeDimScaling?.() || 1;
return (
<div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
{!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index dab53b671..26aa5a121 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -132,6 +132,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
if (tab.element[0].children[1].children.length === 1) {
iconWrap.className = 'lm_iconWrap lm_moreInfo';
+ iconWrap.title = 'click for menu, drag to embed in document';
const dragBtnDown = (e: React.PointerEvent) => {
setupMoveUpEvents(
this,
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 0cc11bf1c..cbcc7c710 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -21,7 +21,7 @@
}
.treeView-bulletIcons {
- width: 100%;
+ margin: auto;
height: 100%;
// changes start here.
@@ -64,6 +64,8 @@
position: relative;
width: fit-content;
min-height: 20px;
+ min-width: 15px;
+ margin-right: 3px;
color: $medium-gray;
border: #80808030 1px solid;
border-radius: 5px;
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 832e102bc..f89aa065b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -433,9 +433,9 @@ export class TreeView extends React.Component<TreeViewProps> {
return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
};
const addDoc = inside ? localAdd : parentAddDoc;
- const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
+ const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
- if (canAdd) {
+ if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this.props.parentTreeView?.doc))) {
this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
@@ -598,7 +598,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
- !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
+ !dataIsComputed && added && Doc.SetContainer(doc, this.doc);
return added;
};
@@ -761,7 +761,7 @@ export class TreeView extends React.Component<TreeViewProps> {
<IconButton color={color} icon={<FontAwesomeIcon icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />} size={Size.XSMALL} />
)
) : (
- <div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}>
+ <div className="treeView-bulletIcons">
<div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
<FontAwesomeIcon
size="sm"
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 1e76d373c..15b6e1d37 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -350,7 +350,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
groupNames.push({ type: 'text', text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: 'div', color: Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: 'div', color: 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8a812c671..9df96fabc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -33,7 +33,6 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
-import { DocumentDecorations } from '../../DocumentDecorations';
import { GestureOverlay } from '../../GestureOverlay';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
@@ -187,7 +186,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
}
@computed get cachedGetTransform(): Transform {
- return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+ return this.getContainerTransform()
+ .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim())
+ .translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
+ .transform(this.cachedGetLocalTransform);
}
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
@@ -862,7 +864,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
if (!e.shiftKey) {
this._eraserLock++;
- this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
+ const segments = this.segmentInkStroke(intersect.inkView, intersect.t);
+ segments.forEach(segment =>
this.forceStrokeGesture(
e,
GestureUtils.Gestures.Stroke,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index cd7bd28e9..4c502021d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -18,7 +18,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { OpenWhere } from '../../nodes/DocumentView';
+import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
@@ -335,7 +335,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
if (!this.props.trySelectCluster(e.shiftKey)) {
- this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
+ !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
} else e.stopPropagation();
}
}
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index 7b2ac5713..ddd99c836 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -71,7 +71,7 @@ $LEFT_MENU_WIDTH: 60px;
$TREE_BULLET_WIDTH: 20px;
$CAROUSEL3D_CENTER_SCALE: 1.3;
-$CAROUSEL3D_SIDE_SCALE: 0.3;
+$CAROUSEL3D_SIDE_SCALE: 0.6;
$CAROUSEL3D_TOP: 15;
:export {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 72c1a9806..ef96e64be 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -702,7 +702,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
}
const cm = ContextMenu.Instance;
- if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
+ if (!cm || (e as any)?.nativeEvent?.SchemaHandled || DocumentView.ExploreMode) return;
if (e && !(e.nativeEvent as any).dash) {
const onDisplay = () => {
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index 394108be4..14a3d16ef 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -226,7 +226,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
} else {
return <Button text="None Selected" type={Type.TERT} color={SettingsManager.userColor} background={SettingsManager.userVariantColor} fillWidth inactive />;
}
- noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
+ noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Carousel3D, CollectionViewType.Stacking, CollectionViewType.NoteTaking];
} else {
text = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result;
// text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 39e27d15b..73a5be90a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -24,7 +24,6 @@ import { ContextMenu } from '../ContextMenu';
import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
import { Colors } from '../global/globalEnums';
-import { LightboxView } from '../LightboxView';
import { CreateImage } from '../nodes/WebBoxRenderer';
import { PDFViewer } from '../pdf/PDFViewer';
import { SidebarAnnos } from '../SidebarAnnos';
@@ -443,7 +442,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
const cm = ContextMenu.Instance;
const options = cm.findByDescription('Options...');
const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
- optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
+ !Doc.noviceMode && optionItems.push({ description: 'Toggle Sidebar Type', event: this.toggleSidebarType, icon: 'expand-arrows-alt' });
!Doc.noviceMode && optionItems.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
//optionItems.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
!options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'asterisk' });
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 48f4c2afd..c5167461b 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -8,7 +8,6 @@ import { NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
-import { ColorScheme } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { DocFocusOptions, DocumentView } from '../DocumentView';
import { FormattedTextBox } from './FormattedTextBox';
@@ -22,7 +21,7 @@ export class DashDocView {
this.dom = document.createElement('span');
this.dom.style.position = 'relative';
this.dom.style.textIndent = '0';
- this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
+ this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, 'lightGray');
this.dom.style.width = node.attrs.width;
this.dom.style.height = node.attrs.height;
this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 360dfca04..6a92d09d9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1637,8 +1637,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
if (!state.selection.empty && !(state.selection instanceof NodeSelection)) this.setupAnchorMenu();
else if (this.props.isContentActive(true)) {
const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY });
- // !this.props.isSelected(true) &&
- editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(pcords?.pos || 0))));
+ let xpos = pcords?.pos || 0;
+ while (xpos > 0 && !state.doc.resolve(xpos).node()?.isTextblock) {
+ xpos = xpos - 1;
+ }
+ editor.dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(xpos))));
let target = e.target as any; // hrefs are stored on the dataset of the <a> node that wraps the hyerlink <span>
while (target && !target.dataset?.targethrefs) target = target.parentElement;
FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc, target?.dataset.nopreview === 'true');
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index bcec2d2bd..2a3b232bd 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -28,6 +28,7 @@ import { CollectionFreeFormView, computeTimelineLayout, MarqueeViewBounds } from
import { CollectionStackedTimeline } from '../../collections/CollectionStackedTimeline';
import { CollectionView } from '../../collections/CollectionView';
import { TabDocView } from '../../collections/TabDocView';
+import { TreeView } from '../../collections/TreeView';
import { ViewBoxBaseComponent } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
import { LightboxView } from '../../LightboxView';
@@ -36,9 +37,6 @@ import { FieldView, FieldViewProps } from '../FieldView';
import { ScriptingBox } from '../ScriptingBox';
import './PresBox.scss';
import { PresEffect, PresEffectDirection, PresMovement, PresStatus } from './PresEnums';
-import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
-import { TreeView } from '../../collections/TreeView';
-import { OverlayView } from '../../OverlayView';
const { Howl } = require('howler');
export interface pinDataTypes {
@@ -1376,7 +1374,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
@computed get paths() {
let pathPoints = '';
this.childDocs.forEach((doc, index) => {
- const tagDoc = Cast(doc.presentation_targetDoc, Doc, null);
+ const tagDoc = PresBox.targetRenderedDoc(doc);
if (tagDoc) {
const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
@@ -1491,6 +1489,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
updateEffect = (effect: PresEffect, bullet: boolean, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (bullet ? (doc.presBulletEffect = effect) : (doc.presentation_effect = effect)));
static _sliderBatch: any;
+ static endBatch = () => {
+ PresBox._sliderBatch.end();
+ document.removeEventListener('pointerup', PresBox.endBatch, true);
+ };
public static inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void, hmargin?: number) => {
return (
<input
@@ -1504,9 +1506,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
className={`toolbar-slider ${active ? '' : 'none'}`}
onPointerDown={e => {
PresBox._sliderBatch = UndoManager.StartBatch('pres slider');
+ document.addEventListener('pointerup', PresBox.endBatch, true);
e.stopPropagation();
}}
- onPointerUp={() => PresBox._sliderBatch.end()}
onChange={e => {
e.stopPropagation();
change(e.target.value);
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 56b97e42f..7170be6cc 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -853,16 +853,24 @@ export namespace Doc {
export async function Zip(doc: Doc, zipFilename = 'dashExport.zip') {
const { clone, map, linkMap } = await Doc.MakeClone(doc);
const proms = new Set<string>();
- function replacer(key: any, value: any) {
+ function replacer(key: any, value: any) {
if (key && ['branchOf', 'cloneOf', 'cursors'].includes(key)) return undefined;
- if (value instanceof ImageField) {
- const extension = value.url.href.replace(/.*\./, '');
- proms.add(value.url.href.replace('.' + extension, '_o.' + extension));
- return SerializationHelper.Serialize(value);
+ if (value?.__type === 'image') {
+ const extension = value.url.replace(/.*\./, '');
+ proms.add(value.url.replace('.' + extension, '_o.' + extension));
+ return SerializationHelper.Serialize(new ImageField(value.url));
}
- if (value instanceof PdfField || value instanceof AudioField || value instanceof VideoField) {
- proms.add(value.url.href);
- return SerializationHelper.Serialize(value);
+ if (value?.__type === 'pdf') {
+ proms.add(value.url);
+ return SerializationHelper.Serialize(new PdfField(value.url));
+ }
+ if (value?.__type === 'audio') {
+ proms.add(value.url);
+ return SerializationHelper.Serialize(new AudioField(value.url));
+ }
+ if (value?.__type === 'video') {
+ proms.add(value.url);
+ return SerializationHelper.Serialize(new VideoField(value.url));
}
if (
value instanceof Doc ||
@@ -889,14 +897,15 @@ export namespace Doc {
const zip = new JSZip();
var count = 0;
- const promArr = Array.from(proms).filter(url => url.startsWith(window.location.origin));
+ const promArr = Array.from(proms).filter(url => url?.startsWith("/files")).map(url => url.replace("/",""))// window.location.origin));
+ console.log(promArr.length);
if (!promArr.length) {
zip.file('docs.json', jsonDocs);
zip.generateAsync({ type: 'blob' }).then(content => saveAs(content, zipFilename));
} else
promArr.forEach((url, i) => {
// loading a file and add it in a zip file
- JSZipUtils.getBinaryContent(url, (err: any, data: any) => {
+ JSZipUtils.getBinaryContent(window.location.origin+"/"+url, (err: any, data: any) => {
if (err) throw err; // or handle the error
// // Generate a directory within the Zip file structure
// const assets = zip.folder("assets");
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c16a1c124..498bec6ed 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -588,11 +588,10 @@ export class MobileInterface extends React.Component {
const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row');
- const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`);
const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
- dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneDashboard!]);
- dashboardDoc.contextMenuLabels = new List<string>(['Toggle Theme Colors', 'Toggle Comic Mode', 'New Dashboard Layout']);
+ dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleComic!, cloneDashboard!]);
+ dashboardDoc.contextMenuLabels = new List<string>(['Toggle Comic Mode', 'New Dashboard Layout']);
Doc.AddDocToList(scens, 'data', dashboardDoc);
};
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 56a9d9b6b..c337144c8 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -262,9 +262,33 @@ export default class UploadManager extends ApiManager {
zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
createReadStream(pathname).pipe(createWriteStream(targetname));
if (extension !== '.pdf') {
- createReadStream(pathname).pipe(createWriteStream(targetname.replace('_o' + extension, '_s' + extension)));
- createReadStream(pathname).pipe(createWriteStream(targetname.replace('_o' + extension, '_m' + extension)));
- createReadStream(pathname).pipe(createWriteStream(targetname.replace('_o' + extension, '_l' + extension)));
+ const { pngs, jpgs } = AcceptableMedia;
+ const resizers = [
+ { resizer: sharp().resize(100, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Small },
+ { resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Medium },
+ { resizer: sharp().resize(900, undefined, { withoutEnlargement: true }), suffix: SizeSuffix.Large },
+ ];
+ let isImage = false;
+ if (pngs.includes(extension)) {
+ resizers.forEach(element => {
+ element.resizer = element.resizer.png();
+ });
+ isImage = true;
+ } else if (jpgs.includes(extension)) {
+ resizers.forEach(element => {
+ element.resizer = element.resizer.jpeg();
+ });
+ isImage = true;
+ }
+ if (isImage) {
+ resizers.forEach(resizer => {
+ createReadStream(pathname)
+ .on('error', e => console.log('Resizing read:' + e))
+ .pipe(resizer.resizer)
+ .on('error', e => console.log('Resizing write: ' + e))
+ .pipe(createWriteStream(targetname.replace('_o' + extension, resizer.suffix + extension)).on('error', e => console.log('Resizing write: ' + e)));
+ });
+ }
}
unlink(pathname, () => {});
} catch (e) {
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index 839091194..ccb709453 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -121,11 +121,11 @@ function determineEnvironment() {
const label = isRelease ? 'release' : 'development';
console.log(`\nrunning server in ${color(label)} mode`);
- // swilkins: I don't think we need to read from ClientUtils.RELEASE anymore. Should be able to invoke process.env.RELEASE
- // on the client side, thanks to dotenv in webpack.config.js
- let clientUtils = fs.readFileSync('./src/client/util/ClientUtils.ts.temp', 'utf8');
- clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(isRelease))}`;
- fs.writeFileSync('./src/client/util/ClientUtils.ts', clientUtils, 'utf8');
+ // // swilkins: I don't think we need to read from ClientUtils.RELEASE anymore. Should be able to invoke process.env.RELEASE
+ // // on the client side, thanks to dotenv in webpack.config.js
+ // let clientUtils = fs.readFileSync('./src/client/util/ClientUtils.ts.temp', 'utf8');
+ // clientUtils = `//AUTO-GENERATED FILE: DO NOT EDIT\n${clientUtils.replace('"mode"', String(isRelease))}`;
+ // fs.writeFileSync('./src/client/util/ClientUtils.ts', clientUtils, 'utf8');
return isRelease;
}