From 4574b7f03ccc85c4bebdbfd9475788456086704f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Aug 2024 12:27:40 -0400 Subject: many changes to add typing in place of 'any's etc --- .../views/collections/CollectionStackingView.tsx | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 56d2a6c9c..03ade6579 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,6 +1,5 @@ /* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -// eslint-disable-next-line import/no-extraneous-dependencies import * as CSS from 'csstype'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; @@ -34,9 +33,7 @@ import { StyleProp } from '../StyleProp'; import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn'; -import { CollectionSubView } from './CollectionSubView'; - -const _global = (window /* browser */ || global) /* node */ as any; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; export type collectionStackingViewProps = { sortFunc?: (a: Doc, b: Doc) => number; @@ -57,8 +54,9 @@ export class CollectionStackingView extends CollectionSubView number; width: () => number; stackedDocTransform: () => Transform }[] = []; // Doesn't look like this field is being used anywhere. Obsolete? _columnStart: number = 0; + _oldWheel: HTMLElement | null = null; - @observable _refList: any[] = []; + @observable _refList: HTMLElement[] = []; // map of node headers to their heights. Used in Masonry @observable _heightMap = new Map(); // Assuming that this is the current css cursor style @@ -85,7 +83,7 @@ export class CollectionStackingView extends CollectionSubView { Doc.BrushDoc(doc); - const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); + const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find(node => node.id === doc[Id]); if (found) { const { top } = found.getBoundingClientRect(); const localTop = this.ScreenToLocalBoxXf().transformPoint(0, top); @@ -344,7 +342,7 @@ export class CollectionStackingView extends CollectionSubView { this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill; }, icon: 'plus' }); // prettier-ignore optionItems.push({ description: `${this.layoutDoc._layout_autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => { this.layoutDoc._layout_autoHeight = !this.layoutDoc._layout_autoHeight; }, icon: 'plus' }); // prettier-ignore optionItems.push({ description: 'Clear All', event: () => { this.dataDoc[this.fieldKey ?? 'data'] = new List([]); } , icon: 'times' }); // prettier-ignore @@ -688,10 +686,9 @@ export class CollectionStackingView extends CollectionSubView this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))); + observer = new ResizeObserver(() => this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))); onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); - _oldWheel: any; render() { TraceMobx(); const editableViewProps = { @@ -722,8 +719,8 @@ export class CollectionStackingView extends CollectionSubView { this._scroll = e.currentTarget.scrollTop; -- cgit v1.2.3-70-g09d2 From b84bdc5a629dfa6310b24dd5eedee2843558b73a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 12 Aug 2024 21:38:22 -0400 Subject: more any -> type fixes --- deploy/mobile/image.html | 16 - deploy/mobile/ink.html | 13 - src/.DS_Store | Bin 10244 -> 10244 bytes src/ClientUtils.ts | 4 - src/ServerUtils.ts | 1 + src/client/util/Scripting.ts | 2 +- src/client/util/type_decls.txt | 224 ++++++ src/client/views/EditableView.tsx | 4 +- src/client/views/GestureOverlay.tsx | 7 +- src/client/views/LightboxView.tsx | 12 +- src/client/views/MainView.tsx | 12 +- src/client/views/OverlayView.tsx | 8 +- src/client/views/PropertiesView.tsx | 8 +- src/client/views/TemplateMenu.tsx | 4 +- src/client/views/collections/CollectionMenu.tsx | 4 +- .../collections/CollectionStackedTimeline.tsx | 4 +- .../views/collections/CollectionStackingView.tsx | 5 +- .../CollectionStackingViewFieldColumn.tsx | 11 +- .../views/collections/CollectionTreeView.tsx | 7 +- src/client/views/collections/TabDocView.tsx | 14 +- src/client/views/collections/TreeView.tsx | 4 +- .../CollectionMulticolumnView.tsx | 8 +- .../CollectionMultirowView.tsx | 6 +- .../collectionMulticolumn/MulticolumnResizer.tsx | 2 +- .../collectionMulticolumn/MultirowResizer.tsx | 2 +- .../collectionSchema/CollectionSchemaView.tsx | 57 +- .../collectionSchema/SchemaColumnHeader.tsx | 4 +- .../collections/collectionSchema/SchemaRowBox.tsx | 4 +- .../collectionSchema/SchemaTableCell.tsx | 32 +- src/client/views/linking/LinkPopup.tsx | 4 +- src/client/views/newlightbox/NewLightboxView.tsx | 14 +- .../RecommendationList/RecommendationList.tsx | 7 +- src/client/views/nodes/KeyValuePair.tsx | 4 +- src/client/views/nodes/LabelBox.tsx | 5 +- src/client/views/nodes/LinkDocPreview.tsx | 7 +- .../views/nodes/MapboxMapBox/MapboxContainer.tsx | 13 +- src/client/views/nodes/trails/PresElementBox.tsx | 18 +- src/client/views/topbar/TopBar.tsx | 8 +- src/fields/Doc.ts | 4 + src/mobile/ImageUpload.scss | 139 ---- src/mobile/ImageUpload.tsx | 170 ---- src/mobile/InkControls.tsx | 0 src/mobile/MobileInkOverlay.scss | 39 - src/mobile/MobileInkOverlay.tsx | 183 ----- src/mobile/MobileInterface.scss | 445 ----------- src/mobile/MobileInterface.tsx | 871 --------------------- src/mobile/MobileMain.tsx | 26 - src/mobile/MobileMenu.scss | 271 ------- webpack.config.js | 6 +- 49 files changed, 382 insertions(+), 2331 deletions(-) delete mode 100644 deploy/mobile/image.html delete mode 100644 deploy/mobile/ink.html create mode 100644 src/client/util/type_decls.txt delete mode 100644 src/mobile/ImageUpload.scss delete mode 100644 src/mobile/ImageUpload.tsx delete mode 100644 src/mobile/InkControls.tsx delete mode 100644 src/mobile/MobileInkOverlay.scss delete mode 100644 src/mobile/MobileInkOverlay.tsx delete mode 100644 src/mobile/MobileInterface.scss delete mode 100644 src/mobile/MobileInterface.tsx delete mode 100644 src/mobile/MobileMain.tsx delete mode 100644 src/mobile/MobileMenu.scss (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/deploy/mobile/image.html b/deploy/mobile/image.html deleted file mode 100644 index cca6f763b..000000000 --- a/deploy/mobile/image.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - Dash Mobile - - - - - - -
- - - - \ No newline at end of file diff --git a/deploy/mobile/ink.html b/deploy/mobile/ink.html deleted file mode 100644 index 938d85794..000000000 --- a/deploy/mobile/ink.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - Test view - - - - -
- - - - \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store index 75cff7b55..7a0b53ce0 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index f9e282993..fc048b155 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -82,10 +82,6 @@ export function returnEmptyFilter() { return [] as string[]; } -export function returnEmptyDoclist() { - return [] as any[]; -} - // eslint-disable-next-line @typescript-eslint/no-namespace export namespace ClientUtils { export const CLICK_TIME = 300; diff --git a/src/ServerUtils.ts b/src/ServerUtils.ts index ade4ca35d..7e821cda2 100644 --- a/src/ServerUtils.ts +++ b/src/ServerUtils.ts @@ -2,6 +2,7 @@ import { Socket } from 'socket.io'; import { Message } from './server/Message'; import { Utils } from './Utils'; +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace ServerUtils { export function Emit(socket: Socket, message: Message, args: T) { Utils.log('Emit', message.Name, args, false); diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 133f8f2ce..b9e0943b6 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -1,7 +1,7 @@ // export const ts = (window as any).ts; // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts' // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts' -import * as typescriptlib from '!!raw-loader!./type_decls.d'; +import * as typescriptlib from './type_decls.d'; import * as ts from 'typescript'; import { Doc, FieldType } from '../../fields/Doc'; import { RefField } from '../../fields/RefField'; diff --git a/src/client/util/type_decls.txt b/src/client/util/type_decls.txt new file mode 100644 index 000000000..1a93bbe59 --- /dev/null +++ b/src/client/util/type_decls.txt @@ -0,0 +1,224 @@ +//@ts-ignore +declare type PropertyKey = string | number | symbol; +interface Array { + length: number; + toString(): string; + toLocaleString(): string; + pop(): T | undefined; + push(...items: T[]): number; + concat(...items: ConcatArray[]): T[]; + concat(...items: (T | ConcatArray)[]): T[]; + join(separator?: string): string; + reverse(): T[]; + shift(): T | undefined; + slice(start?: number, end?: number): T[]; + sort(compareFn?: (a: T, b: T) => number): this; + splice(start: number, deleteCount?: number): T[]; + splice(start: number, deleteCount: number, ...items: T[]): T[]; + unshift(...items: T[]): number; + indexOf(searchElement: T, fromIndex?: number): number; + lastIndexOf(searchElement: T, fromIndex?: number): number; + every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; + forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; + map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; + filter(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]; + filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[]; + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; + reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; + reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; + reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; + reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; + + [n: number]: T; +} + +interface Function { + apply(this: Function, thisArg: any, argArray?: any): any; + call(this: Function, thisArg: any, ...argArray: any[]): any; + bind(this: Function, thisArg: any, ...argArray: any[]): any; + toString(): string; + + prototype: any; + readonly length: number; + + // Non-standard extensions + arguments: any; + caller: Function; +} +interface Boolean { + valueOf(): boolean; +} +interface Number { + toString(radix?: number): string; + toFixed(fractionDigits?: number): string; + toExponential(fractionDigits?: number): string; + toPrecision(precision?: number): string; + valueOf(): number; +} +interface IArguments { + [index: number]: any; + length: number; + callee: Function; +} +interface RegExp { + readonly flags: string; + readonly sticky: boolean; + readonly unicode: boolean; +} +interface Date { + now() : string; +} +interface String { + codePointAt(pos: number): number | undefined; + includes(searchString: string, position?: number): boolean; + endsWith(searchString: string, endPosition?: number): boolean; + normalize(form: "NFC" | "NFD" | "NFKC" | "NFKD"): string; + normalize(form?: string): string; + repeat(count: number): string; + replace(a:any, b:any):string; // bcz: fix this + startsWith(searchString: string, position?: number): boolean; + anchor(name: string): string; + big(): string; + blink(): string; + bold(): string; + fixed(): string; + fontcolor(color: string): string; + fontsize(size: number): string; + fontsize(size: string): string; + italics(): string; + link(url: string): string; + small(): string; + strike(): string; + sub(): string; + sup(): string; +} +interface Object { + constructor: Function; + toString(): string; + toLocaleString(): string; + valueOf(): Object; + hasOwnProperty(v: PropertyKey): boolean; + isPrototypeOf(v: Object): boolean; + propertyIsEnumerable(v: PropertyKey): boolean; +} +interface ConcatArray { + readonly length: number; + readonly [n: number]: T; + join(separator?: string): string; + slice(start?: number, end?: number): T[]; +} +interface URL { + hash: string; + host: string; + hostname: string; + href: string; + readonly origin: string; + password: string; + pathname: string; + port: string; + protocol: string; + search: string; + username: string; + toJSON(): string; +} +interface PromiseLike { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): PromiseLike; +} +interface Promise { + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise; + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; +} + +declare const Update: unique symbol; +declare const Self: unique symbol; +declare const SelfProxy: unique symbol; +declare const DataSym: unique symbol; +declare const HandleUpdate: unique symbol; +declare const Id: unique symbol; +declare const OnUpdate: unique symbol; +declare const Parent: unique symbol; +declare const Copy: unique symbol; +declare const ToScriptString: unique symbol; + +declare abstract class RefField { + readonly [Id]: FieldId; + + constructor(); +} + +declare type FieldId = string; + +declare abstract class ObjectField { + abstract [Copy](): ObjectField; +} + +declare abstract class URLField extends ObjectField { + readonly url: URL; + + constructor(url: string); + constructor(url: URL); +} + +declare class RichTextField extends URLField { + [Copy](): ObjectField; + constructor(data:string, text: string); +} +declare class AudioField extends URLField { [Copy](): ObjectField; } +declare class VideoField extends URLField { [Copy](): ObjectField; } +declare class ImageField extends URLField { [Copy](): ObjectField; } +declare class WebField extends URLField { [Copy](): ObjectField; } +declare class PdfField extends URLField { [Copy](): ObjectField; } + +declare const ComputedField: any; +declare const CompileScript: any; + +// @ts-ignore +declare type Extract = T extends U ? T : never; +declare type Field = number | string | boolean | ObjectField | RefField; +declare type FieldWaiting = T extends undefined ? never : Promise; +declare type FieldResult = Opt | FieldWaiting>; + +declare type Opt = T | undefined; +declare class Doc extends RefField { + constructor(); + + [key: string]: FieldResult; + // [ToScriptString](): string; +} + +declare class List extends ObjectField { + constructor(fields?: T[]); + [index: number]: T | (T extends RefField ? Promise : never); + [Copy](): ObjectField; +} + +declare class InkField extends ObjectField { + constructor(data:Array<{X:number, Y:number}>); + [Copy](): ObjectField; +} + +// @ts-ignore +declare const console: any; + +interface DocumentOptions { } + +declare const Docs: { + ImageDocument(url: string, options?: DocumentOptions): Doc; + VideoDocument(url: string, options?: DocumentOptions): Doc; + TextDocument(options?: DocumentOptions): Doc; + PdfDocument(url: string, options?: DocumentOptions): Doc; + WebDocument(url: string, options?: DocumentOptions): Doc; + HtmlDocument(html: string, options?: DocumentOptions): Doc; + MapDocument(url: string, options?: DocumentOptions): Doc; + KVPDocument(document: Doc, options?: DocumentOptions): Doc; + FreeformDocument(documents: Doc[], options?: DocumentOptions): Doc; + SchemaDocument(columns: string[], documents: Doc[], options?: DocumentOptions): Doc; + TreeDocument(documents: Doc[], options?: DocumentOptions): Doc; + StackingDocument(documents: Doc[], options?: DocumentOptions): Doc; +}; + +declare function idToDoc(id:string):any; +declare function assignDoc(doc:Doc, field:any, id:any):string; +declare function d(...args:any[]):any; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index e02e39b8b..23da5a666 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -26,7 +26,7 @@ export interface EditableProps { /** * The contents to render when not editing */ - contents: string; + contents: JSX.Element | string; fieldContents?: FieldViewProps; fontStyle?: string; fontSize?: number; @@ -301,7 +301,7 @@ export class EditableView extends ObservableReactComponent { fontStyle: this._props.fontStyle, fontSize: this._props.fontSize, }}> - {this._props.fieldContents ? : this._props.contents ? this._props.contents?.valueOf() : ''} + {this._props.fieldContents ? : (this._props.contents ?? '')} ); diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 804c41091..bcd4d1ee5 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -2,9 +2,9 @@ import * as fitCurve from 'fit-curve'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; +import { returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Opt } from '../../fields/Doc'; +import { Doc, Opt, returnEmptyDoclist } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; import { NumCast } from '../../fields/Types'; import { @@ -30,6 +30,7 @@ import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { Transform } from '../util/Transform'; import './GestureOverlay.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { returnEmptyDocViewList } from './StyleProvider'; import { ActiveFillColor, DocumentView } from './nodes/DocumentView'; export enum ToolglassTools { @@ -466,7 +467,7 @@ export class GestureOverlay extends ObservableReactComponent { ScreenToLocalTransform={this.lightboxScreenToLocal} renderDepth={0} suppressSetHeight={!!this._doc._layout_fitWidth} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index ac30c5a14..62ad0a3fb 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -8,9 +8,9 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import ResizeObserver from 'resize-observer-polyfill'; import '../../../node_modules/browndash-components/dist/styles/global.min.css'; -import { ClientUtils, lightOrDark, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; +import { ClientUtils, lightOrDark, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, DocListCast, GetDocFromUrl, Opt } from '../../fields/Doc'; +import { Doc, DocListCast, GetDocFromUrl, Opt, returnEmptyDoclist } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { DocCast, StrCast, toList } from '../../fields/Types'; @@ -627,7 +627,7 @@ export class MainView extends ObservableReactComponent { Document={this.headerBarDoc} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} styleProvider={DefaultStyleProvider} addDocument={this.addHeaderDoc} removeDocument={this.removeHeaderDoc} @@ -662,7 +662,7 @@ export class MainView extends ObservableReactComponent { addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} styleProvider={this._hideUI ? DefaultStyleProvider : undefined} isContentActive={returnTrue} removeDocument={undefined} @@ -766,7 +766,7 @@ export class MainView extends ObservableReactComponent { addDocument={undefined} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={DocumentView.PinDoc} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem || this._sidebarContent.proto === Doc.MyTrails ? DashboardStyleProvider : DefaultStyleProvider} removeDocument={returnFalse} ScreenToLocalTransform={this.mainContainerXf} @@ -799,7 +799,7 @@ export class MainView extends ObservableReactComponent { PanelWidth={this.leftMenuWidth} PanelHeight={this.leftMenuHeight} renderDepth={0} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} focus={emptyFunction} styleProvider={DefaultStyleProvider} isContentActive={returnTrue} diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx index 7bf10467e..5e9677b45 100644 --- a/src/client/views/OverlayView.tsx +++ b/src/client/views/OverlayView.tsx @@ -4,9 +4,9 @@ import { computedFn } from 'mobx-utils'; import * as React from 'react'; import ReactLoading from 'react-loading'; import ResizeObserver from 'resize-observer-polyfill'; -import { returnEmptyDoclist, returnEmptyFilter, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; +import { returnEmptyFilter, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { Utils, emptyFunction } from '../../Utils'; -import { Doc } from '../../fields/Doc'; +import { Doc, returnEmptyDoclist } from '../../fields/Doc'; import { Height, Width } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { NumCast, toList } from '../../fields/Types'; @@ -16,7 +16,7 @@ import { dropActionType } from '../util/DropActionTypes'; import { Transform } from '../util/Transform'; import { ObservableReactComponent } from './ObservableReactComponent'; import './OverlayView.scss'; -import { DefaultStyleProvider } from './StyleProvider'; +import { DefaultStyleProvider, returnEmptyDocViewList } from './StyleProvider'; import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; export type OverlayDisposer = () => void; @@ -226,7 +226,7 @@ export class OverlayView extends ObservableReactComponent { whenChildContentsActiveChanged={emptyFunction} focus={emptyFunction} styleProvider={DefaultStyleProvider} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} childFilters={returnEmptyFilter} diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5952d6641..daa8e1720 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -10,9 +10,9 @@ import * as React from 'react'; import { ColorResult, SketchPicker } from 'react-color'; import * as Icons from 'react-icons/bs'; // {BsCollectionFill, BsFillFileEarmarkImageFill} from "react-icons/bs" import ResizeObserver from 'resize-observer-polyfill'; -import { ClientUtils, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; +import { ClientUtils, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast } from '../../fields/Doc'; +import { Doc, Field, FieldResult, FieldType, HierarchyMapping, NumListCast, Opt, ReverseHierarchyMap, StrListCast, returnEmptyDoclist } from '../../fields/Doc'; import { AclAdmin, DocAcl, DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { InkField } from '../../fields/InkField'; @@ -36,7 +36,7 @@ import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector import { PropertiesDocContextSelector } from './PropertiesDocContextSelector'; import { PropertiesSection } from './PropertiesSection'; import './PropertiesView.scss'; -import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener } from './StyleProvider'; +import { DefaultStyleProvider, SetFilterOpener as SetPropertiesFilterOpener, returnEmptyDocViewList } from './StyleProvider'; import { DocumentView } from './nodes/DocumentView'; import { StyleProviderFuncType } from './nodes/FieldView'; import { OpenWhere } from './nodes/OpenWhere'; @@ -322,7 +322,7 @@ export class PropertiesView extends ObservableReactComponent boolean; createDropTarget: (ele: HTMLDivElement) => void; screenToLocalTransform: () => Transform; - refList: any[]; + refList: HTMLElement[]; } @observer @@ -64,7 +61,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< @observable _heading = ''; @observable _color = ''; - constructor(props: any) { + constructor(props: CSVFieldColumnProps) { super(props); makeObservable(this); this._heading = this._props.headingObject ? this._props.headingObject.heading : this._props.heading; @@ -118,7 +115,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< this._props.pivotField && drop.docs?.forEach(d => Doc.SetInPlace(d, this._props.pivotField, drop.val, false)); return true; }); - getValue = (value: string): any => { + getValue = (value: string) => { const parsed = parseInt(value); if (!isNaN(parsed)) return parsed; if (value.toLowerCase().indexOf('true') > -1) return true; @@ -212,7 +209,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
{colors.map(col => { const palette = PastelSchemaPalette.get(col); - return
this.changeColumnColor(palette!)} />; + return
this.changeColumnColor(palette!)} />; })}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 0557a9102..a60cd98ac 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -2,8 +2,8 @@ import { action, computed, IReactionDisposer, makeObservable, observable, reacti import { observer } from 'mobx-react'; import * as React from 'react'; import ResizeObserver from 'resize-observer-polyfill'; -import { DivHeight, returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../ClientUtils'; -import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; +import { DivHeight, returnAll, returnEmptyFilter, returnFalse, returnNone, returnOne, returnTrue, returnZero } from '../../../ClientUtils'; +import { Doc, DocListCast, Opt, returnEmptyDoclist, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { listSpec } from '../../../fields/Schema'; @@ -25,6 +25,7 @@ import { EditableView } from '../EditableView'; import { DocumentView } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProp'; +import { returnEmptyDocViewList } from '../StyleProvider'; import { CollectionFreeFormView } from './collectionFreeForm'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import './CollectionTreeView.scss'; @@ -369,7 +370,7 @@ export class CollectionTreeView extends CollectionSubView { PanelWidth={this.PanelWidth} PanelHeight={this.PanelHeight} styleProvider={DefaultStyleProvider} - childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist} - childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist} + childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDocViewList} + childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDocViewList} searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist} addDocument={undefined} removeDocument={this.remDocTab} @@ -628,7 +628,7 @@ export class TabDocView extends ObservableReactComponent { dontCenter="y" whenChildContentsActiveChanged={this.whenChildContentActiveChanges} focus={this.focusFunc} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} pinToPres={TabDocView.PinDoc} /> {this.disableMinimap() ? null : } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 54053d038..c0fe7a894 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -4,9 +4,9 @@ import { IconButton, Size } from 'browndash-components'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { ClientUtils, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; +import { ClientUtils, lightOrDark, return18, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Field, FieldResult, FieldType, Opt, StrListCast, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index b8509a005..5125bdb6c 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { Button, IconButton } from 'browndash-components'; @@ -14,7 +12,7 @@ import { SettingsManager } from '../../../util/SettingsManager'; import { Transform } from '../../../util/Transform'; import { undoBatch, undoable } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; -import { CollectionSubView } from '../CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import './CollectionMulticolumnView.scss'; import ResizeBar from './MulticolumnResizer'; import WidthLabel from './MulticolumnWidthLabel'; @@ -42,7 +40,7 @@ const resizerWidth = 8; export class CollectionMulticolumnView extends CollectionSubView() { @observable _startIndex = 0; - constructor(props: any) { + constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); } @@ -305,7 +303,7 @@ export class CollectionMulticolumnView extends CollectionSubView() { whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} addDocTab={this._props.addDocTab} pinToPres={this._props.pinToPres} - dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy' + dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'} /> ); }; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 3fe3d5343..2833ff2f8 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -8,7 +8,7 @@ import { dropActionType } from '../../../util/DropActionTypes'; import { Transform } from '../../../util/Transform'; import { undoBatch } from '../../../util/UndoManager'; import { DocumentView } from '../../nodes/DocumentView'; -import { CollectionSubView } from '../CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import './CollectionMultirowView.scss'; import HeightLabel from './MultirowHeightLabel'; import ResizeBar from './MultirowResizer'; @@ -33,7 +33,7 @@ const resizerHeight = 8; @observer export class CollectionMultirowView extends CollectionSubView() { - constructor(props: any) { + constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); } @@ -284,7 +284,7 @@ export class CollectionMultirowView extends CollectionSubView() { whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged} addDocTab={this._props.addDocTab} pinToPres={this._props.pinToPres} - dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as any} // 'y', 'x', 'xy' + dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'y' | 'x' | 'xy'} /> ); }; diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx index 931e2c5e0..10a6fa2e9 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx @@ -68,7 +68,7 @@ export default class ResizeBar extends React.Component { style={{ pointerEvents: this.props.isContentActive?.() ? 'all' : 'none', width: this.props.width, - backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor), + backgroundColor: !this.props.isContentActive?.() ? '' : (this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) as string), }}>
this.registerResizing(e)} />
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index cff0a8b4c..918365700 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -66,7 +66,7 @@ export default class ResizeBar extends React.Component { style={{ pointerEvents: this.props.isContentActive?.() ? 'all' : 'none', height: this.props.height, - backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor), + backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) as string, }}>
this.registerResizing(e)} />
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 6bea53355..8b0639b3b 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -4,7 +4,7 @@ import { Popup, PopupTrigger, Type } from 'browndash-components'; import { ObservableMap, action, computed, makeObservable, observable, observe, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnEmptyDoclist, returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils'; +import { returnEmptyString, returnFalse, returnIgnore, returnNever, returnTrue, setupMoveUpEvents, smoothScroll } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; import { Doc, DocListCast, Field, FieldType, NumListCast, Opt, StrListCast } from '../../../../fields/Doc'; import { DocData } from '../../../../fields/DocSymbols'; @@ -22,12 +22,12 @@ import { ContextMenu } from '../../ContextMenu'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { StyleProp } from '../../StyleProp'; -import { DefaultStyleProvider } from '../../StyleProvider'; +import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider'; import { Colors } from '../../global/globalEnums'; import { DocumentView } from '../../nodes/DocumentView'; import { FieldViewProps } from '../../nodes/FieldView'; import { FocusViewOptions } from '../../nodes/FocusViewOptions'; -import { CollectionSubView } from '../CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView'; import './CollectionSchemaView.scss'; import { SchemaColumnHeader } from './SchemaColumnHeader'; import { SchemaRowBox } from './SchemaRowBox'; @@ -49,14 +49,14 @@ const defaultColumnKeys: string[] = ['title', 'type', 'author', 'author_date', ' @observer export class CollectionSchemaView extends CollectionSubView() { - private _keysDisposer: any; + private _keysDisposer?: () => void; private _previewRef: HTMLDivElement | null = null; private _makeNewColumn: boolean = false; private _documentOptions: DocumentOptions = new DocumentOptions(); private _tableContentRef: HTMLDivElement | null = null; private _menuTarget = React.createRef(); - constructor(props: any) { + constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); } @@ -76,7 +76,7 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _columnMenuIndex: number | undefined = undefined; @observable _newFieldWarning: string = ''; @observable _makeNewField: boolean = false; - @observable _newFieldDefault: any = 0; + @observable _newFieldDefault: boolean | number | string | undefined = 0; @observable _newFieldType: ColumnType = ColumnType.Number; @observable _menuValue: string = ''; @observable _filterColumnIndex: number | undefined = undefined; @@ -161,11 +161,11 @@ export class CollectionSchemaView extends CollectionSubView() { Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => this.fieldInfos.set(pair[0], pair[1])); this._keysDisposer = observe( this.dataDoc[this.fieldKey ?? 'data'] as List, - (change: any) => { + change => { switch (change.type) { case 'splice': // prettier-ignore - (change as any).added.forEach((doc: Doc) => // for each document added + change.added.filter(doc => doc instanceof Doc).map(doc => doc as Doc).forEach((doc: Doc) => // for each document added Doc.GetAllPrototypes(doc.value as Doc).forEach(proto => // for all of its prototypes (and itself) Object.keys(proto).forEach(action(key => // check if any of its keys are new, and add them !this.fieldInfos.get(key) && this.fieldInfos.set(key, new FInfo("-no description-", key === 'author')))))); @@ -270,7 +270,7 @@ export class CollectionSchemaView extends CollectionSubView() { addRow = (doc: Doc | Doc[]) => this.addDocument(doc); @undoBatch - changeColumnKey = (index: number, newKey: string, defaultVal?: any) => { + changeColumnKey = (index: number, newKey: string, defaultVal?: string | number | boolean) => { if (!this.documentKeys.includes(newKey)) { this.addNewKey(newKey, defaultVal); } @@ -281,7 +281,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @undoBatch - addColumn = (key: string, defaultVal?: any) => { + addColumn = (key: string, defaultVal?: string | number | boolean) => { if (!this.documentKeys.includes(key)) { this.addNewKey(key, defaultVal); } @@ -298,7 +298,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - addNewKey = (key: string, defaultVal: any) => + addNewKey = (key: string, defaultVal?: string | number | boolean) => this.childDocs.forEach(doc => { doc[DocData][key] = defaultVal; }); @@ -317,7 +317,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - startResize = (e: any, index: number) => { + startResize = (e: React.PointerEvent, index: number) => { this._displayColumnWidths = this.storedColumnWidths; setupMoveUpEvents(this, e, moveEv => this.resizeColumn(moveEv, index), this.finishResize, emptyFunction); }; @@ -604,7 +604,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; scrollToDoc = (doc: Doc, options: FocusViewOptions) => { - const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); + const found = this._tableContentRef && Array.from(this._tableContentRef.getElementsByClassName('documentView-node')).find(node => node.id === doc[Id]); if (found) { const rect = found.getBoundingClientRect(); const localRect = this.ScreenToLocalBoxXf().transformBounds(rect.left, rect.top, rect.width, rect.height); @@ -625,9 +625,9 @@ export class CollectionSchemaView extends CollectionSubView() { type="number" name="" id="" - value={this._newFieldDefault ?? 0} + value={Number(this._newFieldDefault ?? 0)} onPointerDown={e => e.stopPropagation()} - onChange={action((e: any) => { + onChange={action(e => { this._newFieldDefault = e.target.value; })} /> @@ -637,11 +637,9 @@ export class CollectionSchemaView extends CollectionSubView() { <> e.stopPropagation()} - onChange={action((e: any) => { + onChange={action(e => { this._newFieldDefault = e.target.checked; })} /> @@ -654,9 +652,9 @@ export class CollectionSchemaView extends CollectionSubView() { type="text" name="" id="" - value={this._newFieldDefault ?? ''} + value={this._newFieldDefault?.toString() ?? ''} onPointerDown={e => e.stopPropagation()} - onChange={action((e: any) => { + onChange={action(e => { this._newFieldDefault = e.target.value; })} /> @@ -683,7 +681,7 @@ export class CollectionSchemaView extends CollectionSubView() { }; @action - setKey = (key: string, defaultVal?: any) => { + setKey = (key: string, defaultVal?: string | number | boolean) => { if (this._makeNewColumn) { this.addColumn(key, defaultVal); } else { @@ -856,16 +854,16 @@ export class CollectionSchemaView extends CollectionSubView() { onKeysPassiveWheel = (e: WheelEvent) => { // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this) - if (!this._oldKeysWheel.scrollTop && e.deltaY <= 0) e.preventDefault(); + if (!this._oldKeysWheel?.scrollTop && e.deltaY <= 0) e.preventDefault(); e.stopPropagation(); }; - _oldKeysWheel: any; + _oldKeysWheel: HTMLDivElement | null = null; @computed get keysDropdown() { return (
{ + onPointerDown={action(e => { e.stopPropagation(); this._makeNewField = true; })}> @@ -880,6 +878,7 @@ export class CollectionSchemaView extends CollectionSubView() { }}> {this._menuKeys.map(key => (
{ e.stopPropagation(); @@ -962,7 +961,7 @@ export class CollectionSchemaView extends CollectionSubView() { {this.renderFilterOptions}
{ + onPointerDown={action(e => { e.stopPropagation(); this.closeFilterMenu(); })}> @@ -1013,7 +1012,7 @@ export class CollectionSchemaView extends CollectionSubView() { screenToLocal = () => this.ScreenToLocalBoxXf().translate(-this.tableWidth, 0); previewWidthFunc = () => this.previewWidth; onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); - _oldWheel: any; + _oldWheel: HTMLDivElement | null = null; render() { return (
this.createDashEventsTarget(ele)} onDrop={this.onExternalDrop.bind(this)} onPointerMove={e => this.onPointerMove(e)}> @@ -1112,7 +1111,7 @@ export class CollectionSchemaView extends CollectionSubView() { childFiltersByRanges={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} styleProvider={DefaultStyleProvider} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} moveDocument={this._props.moveDocument} addDocument={this.addRow} removeDocument={this._props.removeDocument} @@ -1137,7 +1136,7 @@ interface CollectionSchemaViewDocProps { @observer class CollectionSchemaViewDoc extends ObservableReactComponent { - constructor(props: any) { + constructor(props: CollectionSchemaViewDocProps) { super(props); makeObservable(this); } diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 6b5a34ec0..e0ed8d01e 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -18,8 +18,8 @@ export interface SchemaColumnHeaderProps { setSort: (field: string | undefined, desc?: boolean) => void; removeColumn: (index: number) => void; rowHeight: () => number; - resizeColumn: (e: any, index: number) => void; - dragColumn: (e: any, index: number) => boolean; + resizeColumn: (e: React.PointerEvent, index: number) => void; + dragColumn: (e: PointerEvent, index: number) => boolean; openContextMenu: (x: number, y: number, index: number) => void; setColRef: (index: number, ref: HTMLDivElement) => void; } diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 760089ffb..a7e0e916b 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -58,8 +58,8 @@ export class SchemaRowBox extends ViewBoxBaseComponent() { selectCell = (doc: Doc, col: number, shift: boolean, ctrl: boolean) => this.schemaView?.selectCell(doc, col, shift, ctrl); deselectCell = () => this.schemaView?.deselectAllCells(); selectedCells = () => this.schemaView?._selectedDocs; - setColumnValues = (field: any, value: any) => this.schemaView?.setColumnValues(field, value) ?? false; - setSelectedColumnValues = (field: any, value: any) => this.schemaView?.setSelectedColumnValues(field, value) ?? false; + setColumnValues = (field: string, value: string) => this.schemaView?.setColumnValues(field, value) ?? false; + setSelectedColumnValues = (field: string, value: string) => this.schemaView?.setSelectedColumnValues(field, value) ?? false; columnWidth = computedFn((index: number) => () => this.schemaView?.displayColumnWidths[index] ?? CollectionSchemaView._minColWidth); render() { return ( diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index 5874364e0..22506cac1 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,4 +1,3 @@ -/* eslint-disable jsx-a11y/alt-text */ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable no-use-before-define */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -10,10 +9,10 @@ import * as React from 'react'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import Select from 'react-select'; -import { ClientUtils, StopEvent, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils'; +import { ClientUtils, StopEvent, returnEmptyFilter, returnFalse, returnZero } from '../../../../ClientUtils'; import { emptyFunction } from '../../../../Utils'; import { DateField } from '../../../../fields/DateField'; -import { Doc, DocListCast, Field } from '../../../../fields/Doc'; +import { Doc, DocListCast, Field, returnEmptyDoclist } from '../../../../fields/Doc'; import { RichTextField } from '../../../../fields/RichTextField'; import { ColumnType } from '../../../../fields/SchemaHeaderField'; import { BoolCast, Cast, DateCast, DocCast, FieldValue, StrCast, toList } from '../../../../fields/Types'; @@ -22,7 +21,7 @@ import { FInfo, FInfoFieldType } from '../../../documents/Documents'; import { dropActionType } from '../../../util/DropActionTypes'; import { SnappingManager } from '../../../util/SnappingManager'; import { Transform } from '../../../util/Transform'; -import { undoBatch, undoable } from '../../../util/UndoManager'; +import { undoable } from '../../../util/UndoManager'; import { EditableView } from '../../EditableView'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DefaultStyleProvider, returnEmptyDocViewList } from '../../StyleProvider'; @@ -138,7 +137,7 @@ export class SchemaTableCell extends ObservableReactComponent selectedCell(this._props) && this._props.autoFocus && r?.setIsFocused(true)} oneLine={this._props.oneLine} allowCRs={this._props.allowCRs} - contents={undefined} + contents={''} fieldContents={fieldProps} editing={selectedCell(this._props) ? undefined : false} GetValue={() => Field.toKeyValueString(fieldProps.Document, this._props.fieldKey, SnappingManager.MetaKey)} @@ -209,7 +208,7 @@ export class SchemaTableCell extends ObservableReactComponent { - constructor(props: any) { + constructor(props: SchemaTableCellProps) { super(props); makeObservable(this); } @@ -276,7 +275,7 @@ export class SchemaImageCell extends ObservableReactComponent { - constructor(props: any) { + constructor(props: SchemaTableCellProps) { super(props); makeObservable(this); } @@ -324,7 +323,7 @@ export class SchemaDateCell extends ObservableReactComponent { - constructor(props: any) { + constructor(props: SchemaTableCellProps) { super(props); makeObservable(this); } @@ -343,7 +342,7 @@ export class SchemaRTFCell extends ObservableReactComponent { - constructor(props: any) { + constructor(props: SchemaTableCellProps) { super(props); makeObservable(this); } @@ -356,18 +355,19 @@ export class SchemaBoolCell extends ObservableReactComponent | undefined) => { - if ((value?.nativeEvent as any).shiftKey) { + onChange={undoable((value: React.ChangeEvent | undefined) => { + if ((value?.nativeEvent as MouseEvent | PointerEvent).shiftKey) { this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); } else Doc.SetField(this._props.Document, this._props.fieldKey.replace(/^_/, ''), (color === 'black' ? '=' : '') + (value?.target?.checked.toString() ?? '')); - })} + }, 'set bool cell')} /> + Field.toKeyValueString(this._props.Document, this._props.fieldKey)} - SetValue={undoBatch((value: string, shiftDown?: boolean, enterKey?: boolean) => { + SetValue={undoable((value: string, shiftDown?: boolean, enterKey?: boolean) => { if (shiftDown && enterKey) { this._props.setColumnValues(this._props.fieldKey.replace(/^_/, ''), value); this._props.finishEdit?.(); @@ -376,7 +376,7 @@ export class SchemaBoolCell extends ObservableReactComponent
); @@ -384,7 +384,7 @@ export class SchemaBoolCell extends ObservableReactComponent { - constructor(props: any) { + constructor(props: SchemaTableCellProps) { super(props); makeObservable(this); } diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index 76a8396ff..9ce5f8fc9 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -1,9 +1,9 @@ /* eslint-disable react/require-default-props */ import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; +import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc } from '../../../fields/Doc'; +import { Doc, returnEmptyDoclist } from '../../../fields/Doc'; import { Transform } from '../../util/Transform'; import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; import { SearchBox } from '../search/SearchBox'; diff --git a/src/client/views/newlightbox/NewLightboxView.tsx b/src/client/views/newlightbox/NewLightboxView.tsx index c86ddb745..b060fc0b6 100644 --- a/src/client/views/newlightbox/NewLightboxView.tsx +++ b/src/client/views/newlightbox/NewLightboxView.tsx @@ -1,17 +1,15 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnEmptyDoclist, returnEmptyFilter, returnTrue } from '../../../ClientUtils'; +import { returnEmptyFilter, returnTrue } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { CreateLinkToActiveAudio, Doc, DocListCast, Opt } from '../../../fields/Doc'; +import { CreateLinkToActiveAudio, Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast, toList } from '../../../fields/Types'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { GestureOverlay } from '../GestureOverlay'; -import { DefaultStyleProvider } from '../StyleProvider'; +import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; import { DocumentView } from '../nodes/DocumentView'; import { OpenWhere } from '../nodes/OpenWhere'; import { ExploreView } from './ExploreView'; @@ -68,7 +66,7 @@ export class NewLightboxView extends React.Component { @action public static SetCookie(cookie: string) { if (this.LightboxDoc && cookie) { - this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`); + this._docFilters = (f => (this._docFilters ? ([this._docFilters.push(f) as unknown, this._docFilters][1] as string[]) : [f]))(`cookies:${cookie}:provide`); } } public static AddDocTab = (docsIn: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => { @@ -264,7 +262,7 @@ export class NewLightboxView extends React.Component { styleProvider={DefaultStyleProvider} ScreenToLocalTransform={this.newLightboxScreenToLocal} renderDepth={0} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} childFilters={this.docFilters} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} @@ -320,7 +318,7 @@ export class NewLightboxView extends React.Component {
)}
- +
); diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx index dc3339cd3..27413bac3 100644 --- a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx @@ -1,6 +1,4 @@ /* eslint-disable react/jsx-props-no-spreading */ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ /* eslint-disable guard-for-in */ import { IconButton, Size, Type } from 'browndash-components'; import * as React from 'react'; @@ -168,7 +166,8 @@ export function RecommendationList() {
{keywordsLoc && keywordsLoc.map((word, ind) => ( -
+
+ {' '} {word} )}
-
{recs && recs.map((rec: IRecommendation) => )}
+
{recs && recs.map(rec => )}
); } diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx index 8e74e1ca2..85aff04c3 100644 --- a/src/client/views/nodes/KeyValuePair.tsx +++ b/src/client/views/nodes/KeyValuePair.tsx @@ -2,9 +2,9 @@ import { Tooltip } from '@mui/material'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnZero } from '../../../ClientUtils'; +import { returnEmptyFilter, returnFalse, returnZero } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, Field } from '../../../fields/Doc'; +import { Doc, Field, returnEmptyDoclist } from '../../../fields/Doc'; import { DocCast } from '../../../fields/Types'; import { DocumentOptions, FInfo } from '../../documents/Documents'; import { Transform } from '../../util/Transform'; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index d33d12603..e39caecb6 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -1,10 +1,11 @@ import { Property } from 'csstype'; -import { action, computed, makeObservable, trace } from 'mobx'; +import { action, computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import * as textfit from 'textfit'; import { Field, FieldType } from '../../../fields/Doc'; import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; @@ -100,7 +101,7 @@ export class LabelBox extends ViewBoxBaseComponent() { return textfitParams; }; render() { - trace(); + TraceMobx(); const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes const label = this.Title.startsWith('#') ? null : this.Title; return ( diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 10ff86a38..5026f52fb 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -4,9 +4,9 @@ import { action, computed, makeObservable, observable, runInAction } from 'mobx' import { observer } from 'mobx-react'; import * as React from 'react'; import wiki from 'wikijs'; -import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils'; +import { returnEmptyFilter, returnEmptyString, returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; -import { Doc, Opt } from '../../../fields/Doc'; +import { Doc, Opt, returnEmptyDoclist } from '../../../fields/Doc'; import { Cast, DocCast, NumCast, PromiseValue, StrCast } from '../../../fields/Types'; import { DocServer } from '../../DocServer'; import { DocumentType } from '../../documents/DocumentTypes'; @@ -17,6 +17,7 @@ import { SearchUtil } from '../../util/SearchUtil'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; +import { returnEmptyDocViewList } from '../StyleProvider'; import { DocumentView } from './DocumentView'; import { StyleProviderFuncType } from './FieldView'; import './LinkDocPreview.scss'; @@ -286,7 +287,7 @@ export class LinkDocPreview extends ObservableReactComponent deselectPin = () => { if (this.selectedPin) { // Removes filter - Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove'); - Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove'); + Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove'); + Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove'); Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); const temp = this.selectedPin; @@ -536,8 +537,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent deleteSelectedPin = undoable(() => { if (this.selectedPin) { // Removes filter - Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove'); - Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove'); + Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove'); + Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove'); Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove'); this.removePushpin(this.selectedPin); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 25adfba23..a76805960 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -1,11 +1,9 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils'; +import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils'; import { Doc, DocListCast, Opt } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; @@ -23,6 +21,7 @@ import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; import { PinDocView } from '../../PinFuncs'; import { StyleProp } from '../../StyleProp'; +import { returnEmptyDocViewList } from '../../StyleProvider'; import { DocumentView } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { PresBox } from './PresBox'; @@ -105,7 +104,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { embedHeight = () => this.collapsedHeight + this.expandViewHeight; embedWidth = () => this._props.PanelWidth() / 2; // prettier-ignore - styleProvider = ( doc: Doc | undefined, props: Opt, property: string ): any => + styleProvider = ( doc: Doc | undefined, props: Opt, property: string ) => (property === StyleProp.Opacity ? 1 : this._props.styleProvider?.(doc, props, property)); /** * The function that is responsible for rendering a preview or not for this @@ -123,7 +122,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { hideLinkButton ScreenToLocalTransform={Transform.Identity} renderDepth={this._props.renderDepth + 1} - containerViewPath={returnEmptyDoclist} + containerViewPath={returnEmptyDocViewList} childFilters={this._props.childFilters} childFiltersByRanges={this._props.childFiltersByRanges} searchFilterDocs={this._props.searchFilterDocs} @@ -144,6 +143,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { const childDocs = DocListCast(this.targetDoc.data); const groupSlides = childDocs.map((doc: Doc, ind: number) => (
{ e.stopPropagation(); @@ -156,7 +156,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { StrCast(doc.title)} SetValue={(value: string) => { @@ -179,7 +179,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { @action headerDown = (e: React.PointerEvent) => { - const element = e.target as any; + const element = e.target as HTMLDivElement; e.stopPropagation(); e.preventDefault(); if (element && !(e.ctrlKey || e.metaKey || e.button === 2)) { @@ -580,7 +580,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { className={`presItem-slide ${isCurrent ? 'active' : ''}${activeItem.runProcess ? ' testingv2' : ''}`} style={{ display: 'infline-block', - backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor), + backgroundColor: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string, // layout_boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined, border: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isCurrent ? presBoxColor + ' solid 2.5px' : undefined) : undefined, }}> @@ -602,7 +602,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { } }} onClick={e => e.stopPropagation()}>{`${this.indexInPres + 1}. `}
- StrCast(activeItem.title)} SetValue={this.onSetValue} /> + StrCast(activeItem.title)} SetValue={this.onSetValue} />
{/*
{"Movement speed"}
}>
{this.transition}
*/} {/*
{"Duration"}
}>
{this.duration}
*/} diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index e558e14e3..a85606bc4 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -5,8 +5,8 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { Flip } from 'react-awesome-reveal'; import { FaBug } from 'react-icons/fa'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; -import { Doc, DocListCast } from '../../../fields/Doc'; +import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; +import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc'; import { AclAdmin, DashVersion } from '../../../fields/DocSymbols'; import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; @@ -33,11 +33,11 @@ import './TopBar.scss'; * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard. */ @observer -export class TopBar extends ObservableReactComponent<{}> { +export class TopBar extends ObservableReactComponent { // eslint-disable-next-line no-use-before-define static Instance: TopBar; @observable private _flipDocumentation = 0; - constructor(props: any) { + constructor(props: object) { super(props); makeObservable(this); TopBar.Instance = this; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e6a95fd30..7e7c319bf 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1658,6 +1658,10 @@ export namespace Doc { } } +export function returnEmptyDoclist() { + return [] as Doc[]; +} + export function RTFIsFragment(html: string) { return html.indexOf('data-pm-slice') !== -1; } diff --git a/src/mobile/ImageUpload.scss b/src/mobile/ImageUpload.scss deleted file mode 100644 index e4156ee8e..000000000 --- a/src/mobile/ImageUpload.scss +++ /dev/null @@ -1,139 +0,0 @@ -@import '../client/views/global/globalCssVariables.module.scss'; - -.imgupload_cont { - display: flex; - justify-content: center; - flex-direction: column; - align-items: center; - max-width: 400px; - min-width: 400px; - - .upload_label { - font-weight: 700; - color: black; - background-color: rgba(0, 0, 0, 0); - border: solid 3px black; - margin: 10px; - font-size: 30; - height: 70px; - width: 80%; - display: flex; - font-family: sans-serif; - text-transform: uppercase; - justify-content: center; - flex-direction: column; - border-radius: 10px; - } - - .file { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - direction: ltr; - } - - .button_file { - text-align: center; - height: 50%; - width: 50%; - background-color: paleturquoise; - color: grey; - font-size: 3em; - } - - .inputfile { - width: 0.1px; - height: 0.1px; - opacity: 0; - overflow: hidden; - position: absolute; - z-index: -1; - } - - .inputfile + label { - font-weight: 700; - color: black; - background-color: rgba(0, 0, 0, 0); - border: solid 3px black; - margin: 10px; - font-size: 30; - height: 70px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-top: 30px; - width: 80%; - display: flex; - font-family: sans-serif; - text-transform: uppercase; - justify-content: center; - flex-direction: column; - border-radius: 10px; - } - - .inputfile.active + label { - font-style: italic; - color: black; - background-color: lightgreen; - border: solid 3px darkgreen; - } - - .status { - font-size: 2em; - } -} - -.image-upload { - top: 100%; - opacity: 0; -} - -.image-upload.active { - top: 0; - position: absolute; - z-index: 999; - height: 100vh; - width: 100vw; - opacity: 1; -} - -.uploadContainer { - top: 40; - position: absolute; - z-index: 1000; - height: 20vh; - width: 80vw; - opacity: 1; -} - -.closeUpload { - position: absolute; - border-radius: 10px; - top: 3; - color: black; - font-size: 30; - right: 3; - z-index: 1002; - padding: 0px 3px; - background: rgba(0, 0, 0, 0); - transition: 0.5s ease all; - border: 0px solid; -} - -.loadingImage { - display: inline-flex; - width: max-content; -} - -.loadingSlab { - position: relative; - width: 30px; - height: 30px; - margin: 10; - border-radius: 20px; - opacity: 0.2; - background-color: black; - transition: - all 2s, - opacity 1.5s; -} diff --git a/src/mobile/ImageUpload.tsx b/src/mobile/ImageUpload.tsx deleted file mode 100644 index 36c0d6a4d..000000000 --- a/src/mobile/ImageUpload.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import * as rp from 'request-promise'; -import { ClientUtils } from '../ClientUtils'; -import { DocServer } from '../client/DocServer'; -import { Networking } from '../client/Network'; -import { Docs } from '../client/documents/Documents'; -import { MainViewModal } from '../client/views/MainViewModal'; -import { Doc, Opt } from '../fields/Doc'; -import { List } from '../fields/List'; -import { listSpec } from '../fields/Schema'; -import { Cast } from '../fields/Types'; -import './ImageUpload.scss'; - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const { DFLT_IMAGE_NATIVE_DIM } = require('../client/views/global/globalCssVariables.module.scss'); // prettier-ignore - -export interface ImageUploadProps { - Document: Doc; // Target document for upload (upload location) -} - -const inputRef = React.createRef(); -const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', '')); - -@observer -export class Uploader extends React.Component { - @observable nm: string = 'Choose files'; // Text of 'Choose Files' button - @observable process: string = ''; // Current status of upload - @observable private dialogueBoxOpacity = 1; - - onClick = async () => { - try { - // eslint-disable-next-line react/destructuring-assignment - const col = this.props.Document; - await Docs.Prototypes.initialize(); - const imgPrev = document.getElementById('img_preview'); - this.setOpacity(1, '1'); // Slab 1 - if (imgPrev && inputRef.current) { - const { files } = inputRef.current; - this.setOpacity(2, '1'); // Slab 2 - if (files && files.length !== 0) { - this.process = 'Uploading Files'; - for (let index = 0; index < files.length; ++index) { - const file = files[index]; - // eslint-disable-next-line no-await-in-loop - const res = await Networking.UploadFilesToServer({ file }); - this.setOpacity(3, '1'); // Slab 3 - // For each item that the user has selected - res.map(async ({ result }) => { - const { name } = file; - if (result instanceof Error) { - return; - } - const path = result.accessPaths.agnostic.client; - let doc = null; - // Case 1: File is a video - if (file.type === 'video/mp4') { - doc = Docs.Create.VideoDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name }); - // Case 2: File is a PDF document - } else if (file.type === 'application/pdf') { - doc = Docs.Create.PdfDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name }); - // Case 3: File is another document type (most likely Image) - } else { - doc = Docs.Create.ImageDocument(path, { _nativeWidth: defaultNativeImageDim, _width: 400, title: name }); - } - this.setOpacity(4, '1'); // Slab 4 - const docidsRes = await rp.get(ClientUtils.prepend('/getUserDocumentIds')); - if (!docidsRes) { - throw new Error('No user id returned'); - } - const field = await DocServer.GetRefField(JSON.parse(docidsRes).userDocumentId); - let pending: Opt; - if (field instanceof Doc) { - pending = col; - } - if (pending) { - const data = Cast(pending.data, listSpec(Doc)); - if (data) data.push(doc); - else pending.data = new List([doc]); - this.setOpacity(5, '1'); // Slab 5 - this.process = 'File ' + (index + 1).toString() + ' Uploaded'; - this.setOpacity(6, '1'); // Slab 6 - } - if (index + 1 === files.length) { - this.process = 'Uploads Completed'; - this.setOpacity(7, '1'); // Slab 7 - } - }); - } - // Case in which the user pressed upload and no files were selected - } else { - this.process = 'No file selected'; - } - // Three seconds after upload the menu will reset - setTimeout(this.clearUpload, 3000); - } - } catch (error) { - console.log(JSON.stringify(error)); - } - }; - - // Returns the upload interface for mobile - private get uploadInterface() { - return ( -
-
this.closeUpload()}> - -
- - - -
- Upload -
- -
-
-
-
-
-
-
-
-
-

{this.process}

-
- ); - } - - // Updates label after a files is selected (so user knows a file is uploaded) - inputLabel = async () => { - const files: FileList | null = await inputRef.current!.files; - if (files && files.length === 1) { - this.nm = files[0].name; - } else if (files && files.length > 1) { - this.nm = files.length.toString() + ' files selected'; - } - }; // Loops through load icons, and resets buttons - @action - clearUpload = () => { - for (let i = 1; i < 8; i++) { - this.setOpacity(i, '0.2'); - } - this.nm = 'Choose files'; - - if (inputRef.current) { - inputRef.current.value = ''; - } - this.process = ''; - }; - - // Clears the upload and closes the upload menu - closeUpload = () => { - this.clearUpload(); - }; - - // Handles the setting of the loading bar - setOpacity = (index: number, opacity: string) => { - const slab = document.getElementById('slab' + index); - if (slab) slab.style.opacity = opacity; - }; - - render() { - return ; - } -} diff --git a/src/mobile/InkControls.tsx b/src/mobile/InkControls.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/mobile/MobileInkOverlay.scss b/src/mobile/MobileInkOverlay.scss deleted file mode 100644 index b9c1fb146..000000000 --- a/src/mobile/MobileInkOverlay.scss +++ /dev/null @@ -1,39 +0,0 @@ -.mobileInkOverlay { - border: 10px dashed red; - background-color: rgba(0, 0, 0, .05); -} - -.mobileInkOverlay-border { - // background-color: rgba(0, 255, 0, .4); - position: absolute; - pointer-events: auto; - cursor: pointer; - - &.top { - width: calc(100% + 20px); - height: 10px; - top: -10px; - left: -10px; - } - - &.left { - width: 10px; - height: calc(100% + 20px); - top: -10px; - left: -10px; - } - - &.right { - width: 10px; - height: calc(100% + 20px); - top: -10px; - right: -10px; - } - - &.bottom { - width: calc(100% + 20px); - height: 10px; - bottom: -10px; - left: -10px; - } -} \ No newline at end of file diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx deleted file mode 100644 index 6babd2f39..000000000 --- a/src/mobile/MobileInkOverlay.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import { action, observable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { DocServer } from '../client/DocServer'; -import { DragManager } from '../client/util/DragManager'; -import { Doc } from '../fields/Doc'; -import { Gestures } from '../pen-gestures/GestureTypes'; -import { GestureContent, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from '../server/Message'; -import './MobileInkOverlay.scss'; - -@observer -export default class MobileInkOverlay extends React.Component { - public static Instance: MobileInkOverlay; - - @observable private _scale: number = 1; - @observable private _width: number = 0; - @observable private _height: number = 0; - @observable private _x: number = -300; - @observable private _y: number = -300; - @observable private _text: string = ''; - - @observable private _offsetX: number = 0; - @observable private _offsetY: number = 0; - @observable private _isDragging: boolean = false; - private _mainCont: React.RefObject = React.createRef(); - - constructor(props: Readonly<{}>) { - super(props); - MobileInkOverlay.Instance = this; - } - - initialSize(mobileWidth: number, mobileHeight: number) { - const maxWidth = window.innerWidth - 30; - const maxHeight = window.innerHeight - 30; // -30 for padding - if (mobileWidth > maxWidth || mobileHeight > maxHeight) { - const scale = Math.min(maxWidth / mobileWidth, maxHeight / mobileHeight); - return { width: mobileWidth * scale, height: mobileHeight * scale, scale: scale }; - } - return { width: mobileWidth, height: mobileHeight, scale: 1 }; - } - - @action - initMobileInkOverlay(content: MobileInkOverlayContent) { - const { width, height, text } = content; - const scaledSize = this.initialSize(width ? width : 0, height ? height : 0); - this._width = scaledSize.width; - this._height = scaledSize.height; - this._scale = scaledSize.scale; - this._x = 300; // TODO: center on screen - this._y = 25; // TODO: center on screen - this._text = text ? text : ''; - } - - @action - updatePosition(content: UpdateMobileInkOverlayPositionContent) { - const { dx, dy, dsize } = content; - if (dx) this._x += dx; - if (dy) this._y += dy; - // TODO: scale dsize - } - - drawStroke = (content: GestureContent) => { - // TODO: figure out why strokes drawn in corner of mobile interface dont get inserted - - const { points, bounds } = content; - console.log('received points', points, bounds); - - const B = { - right: bounds.right * this._scale + this._x, - left: bounds.left * this._scale + this._x, // TODO: scale - bottom: bounds.bottom * this._scale + this._y, - top: bounds.top * this._scale + this._y, // TODO: scale - width: bounds.width * this._scale, - height: bounds.height * this._scale, - }; - - const target = document.elementFromPoint(this._x + 10, this._y + 10); - target?.dispatchEvent( - new CustomEvent('dashOnGesture', { - bubbles: true, - detail: { - points: points, - gesture: Gestures.Stroke, - bounds: B, - }, - }) - ); - }; - - uploadDocument = async (content: MobileDocumentUploadContent) => { - const { docId } = content; - const doc = await DocServer.GetRefField(docId); - - if (doc && doc instanceof Doc) { - const target = document.elementFromPoint(this._x + 10, this._y + 10); - const dragData = new DragManager.DocumentDragData([doc]); - const complete = new DragManager.DragCompleteEvent(false, dragData); - - if (target) { - console.log('dispatching upload doc!!!!', target, doc); - target.dispatchEvent( - new CustomEvent('dashOnDrop', { - bubbles: true, - detail: { - x: this._x, - y: this._y, - complete: complete, - altKey: false, - metaKey: false, - ctrlKey: false, - shiftKey: false, - embedKey: false, - }, - }) - ); - } else { - alert('TARGET IS UNDEFINED'); - } - } - }; - - @action - dragStart = (e: React.PointerEvent) => { - document.removeEventListener('pointermove', this.dragging); - document.removeEventListener('pointerup', this.dragEnd); - document.addEventListener('pointermove', this.dragging); - document.addEventListener('pointerup', this.dragEnd); - - this._isDragging = true; - this._offsetX = e.pageX - this._mainCont.current!.getBoundingClientRect().left; - this._offsetY = e.pageY - this._mainCont.current!.getBoundingClientRect().top; - - e.preventDefault(); - e.stopPropagation(); - }; - - @action - dragging = (e: PointerEvent) => { - const x = e.pageX - this._offsetX; - const y = e.pageY - this._offsetY; - - // TODO: don't allow drag over library? - this._x = Math.min(Math.max(x, 0), window.innerWidth - this._width); - this._y = Math.min(Math.max(y, 0), window.innerHeight - this._height); - - e.preventDefault(); - e.stopPropagation(); - }; - - @action - dragEnd = (e: PointerEvent) => { - document.removeEventListener('pointermove', this.dragging); - document.removeEventListener('pointerup', this.dragEnd); - - this._isDragging = false; - - e.preventDefault(); - e.stopPropagation(); - }; - - render() { - return ( -
-

{this._text}

-
-
-
-
-
- ); - } -} diff --git a/src/mobile/MobileInterface.scss b/src/mobile/MobileInterface.scss deleted file mode 100644 index 4b32c3da0..000000000 --- a/src/mobile/MobileInterface.scss +++ /dev/null @@ -1,445 +0,0 @@ -$navbar-height: 120px; -$pathbar-height: 50px; - -@media only screen and (max-device-width: 480px) { - * { - margin: 0px; - padding: 0px; - box-sizing: border-box; - font-family: sans-serif; - } -} - -body { - overflow: hidden; -} - -.mobileInterface-container { - height: 100%; - position: relative; - touch-action: none; - width: 100%; - - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -// Topbar of Dash Mobile -.navbar { - position: fixed; - top: 0px; - left: 0px; - width: 100vw; - height: $navbar-height; - background-color: whitesmoke; - z-index: 150; - - .cover { - position: absolute; - right: 0px; - top: 0px; - height: 120px; - width: 120px; - background-color: whitesmoke; - z-index: 200; - } - - .toggle-btn { - position: absolute; - right: 20px; - top: 30px; - height: 70px; - width: 70px; - transition: all 400ms ease-in-out 200ms; - z-index: 180; - } - - .background { - position: absolute; - right: 0px; - top: 0px; - height: 120px; - width: 120px; - //border: 1px solid black; - } - - .background.active { - background-color: lightgrey; - } - - .toggle-btn-home { - right: -200px; - } - - .header { - position: absolute; - top: 50%; - top: calc(9px + 50%); - right: 50%; - transform: translate(50%, -50%); - font-size: 40; - font-weight: 700; - text-align: center; - user-select: none; - text-transform: uppercase; - font-family: Arial, Helvetica, sans-serif; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - direction: ltr; - width: 600px; - } - - .toggle-btn span { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 70%; - height: 4px; - background: black; - transition: all 200ms ease; - z-index: 180; - } - - .toggle-btn span:nth-child(1) { - transition: top 200ms ease-in-out; - top: 30%; - } - - .toggle-btn span:nth-child(3) { - transition: top 200ms ease-in-out; - top: 70%; - } - - .toggle-btn.active { - transition: transform 200ms ease-in-out 200ms; - transform: rotate(135deg); - } - - .toggle-btn.active span:nth-child(1) { - top: 50%; - } - - .toggle-btn.active span:nth-child(2) { - transform: translate(-50%, -50%) rotate(90deg); - } - - .toggle-btn.active span:nth-child(3) { - top: 50%; - } -} - -.sidebar { - position: fixed; - top: 120px; - opacity: 0; - right: -100%; - width: 80%; - height: calc(80% - (120px)); - z-index: 101; - background-color: whitesmoke; - transition: all 400ms ease 50ms; - padding: 20px; - box-shadow: 0 0 5px 5px grey; - - .item { - width: 100%; - padding: 13px 12px; - border-bottom: 1px solid rgba(200, 200, 200, 0.7); - font-family: Arial, Helvetica, sans-serif; - font-style: normal; - font-weight: normal; - user-select: none; - display: inline-flex; - font-size: 35px; - text-transform: uppercase; - color: black; - } - - .ink:focus { - outline: 1px solid blue; - } - - .sidebarButtons { - top: 80px; - position: relative; - } -} - - - - - - -.blanket { - position: fixed; - top: 120px; - opacity: 0.5; - right: -100%; - width: 100%; - height: calc(100% - (120px)); - z-index: 101; - background-color: grey; - padding: 20px; -} - -.blanket.active { - position: absolute; - right: 0%; - z-index: 100; -} - -.home { - position: absolute; - top: 30px; - left: 30px; - font-size: 60; - user-select: none; - text-transform: uppercase; - font-family: Arial, Helvetica, sans-serif; - z-index: 200; -} - -.item-type { - display: inline; - text-transform: lowercase; - margin-left: 20px; - font-size: 35px; - font-style: italic; - color: rgb(28, 28, 28); -} - -.item-title { - max-width: 70%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.right { - margin-left: 20px; - z-index: 200; -} - -.open { - right: 20px; - font-size: 35; - position: absolute; -} - -.left { - width: 100%; - height: 100%; -} - - - -.sidebar.active { - position: absolute; - right: 0%; - opacity: 1; - z-index: 101; -} - -.back { - position: absolute; - left: 42px; - top: 0; - background: #1a1a1a; - width: 50px; - height: 100%; - display: flex; - justify-content: center; - text-align: center; - flex-direction: column; - align-items: center; - border-radius: 10px; - font-size: 25px; - user-select: none; - z-index: 100; -} - -.pathbar { - position: fixed; - top: 118px; - left: 0px; - background: #1a1a1a; - z-index: 120; - border-radius: 0px; - width: 100%; - height: 80px; - overflow: hidden; - - .pathname { - position: relative; - font-size: 25; - top: 50%; - width: 86%; - left: 12%; - color: whitesmoke; - transform: translate(0%, -50%); - z-index: 20; - font-family: sans-serif; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - text-align: left; - text-transform: uppercase; - } - - .scrollmenu { - overflow: auto; - width: 100%; - height: 100%; - white-space: nowrap; - display: inline-flex; - } - - .hidePath { - position: absolute; - height: 100%; - width: 200px; - left: 0px; - top: 0px; - background-image: linear-gradient(to right, #1a1a1a, rgba(0, 0, 0, 0)); - text-align: center; - user-select: none; - z-index: 99; - pointer-events: none; - } - - .pathbarItem { - position: relative; - display: flex; - align-items: center; - color: whitesmoke; - text-align: center; - justify-content: center; - user-select: none; - transform: translate(100px, 0px); - font-size: 30px; - padding: 10px; - text-transform: uppercase; - - .pathbarText { - font-family: sans-serif; - text-align: center; - height: 50px; - padding: 10px; - font-size: 30px; - border-radius: 10px; - text-transform: uppercase; - margin-left: 20px; - position: relative; - } - - .pathIcon { - transform: translate(0px, 0px); - position: relative; - } - } -} - - -/** -* docButton appears at the bottom of mobile document -* Buttons include: pin to presentation, download, upload, reload -*/ -.docButton { - position: relative; - width: 100px; - display: flex; - height: 100px; - font-size: 70px; - text-align: center; - border: 3px solid black; - margin: 20px; - z-index: 100; - border-radius: 100%; - justify-content: center; - flex-direction: column; - align-items: center; -} - -.docButtonContainer { - top: 80%; - position: absolute; - display: flex; - transform: translate(-50%, 0); - left: 50%; - z-index: 100; -} - -.toolbar { - left: 50%; - transform: translate(-50%); - position: absolute; - height: max-content; - top: 0px; - border-radius: 20px; - background-color: lightgrey; - opacity: 0; - transition: all 400ms ease 50ms; -} - -.toolbar.active { - display: inline-block; - width: 300px; - padding: 5px; - opacity: 1; - height: max-content; - top: -450px; -} - -.colorSelector { - position: absolute; - top: 550px; - left: 280px; - transform: translate(-50%, 0); - z-index: 100; - display: inline-flex; - width: max-content; - height: max-content; - pointer-events: all; - font-size: 80px; - user-select: none; -} - -// Menu buttons for toggling between list and icon view -.homeSwitch { - position: fixed; - top: 212; - right: 36px; - display: inline-flex; - width: max-content; - z-index: 99; - height: 70px; - - .list { - width: 70px; - height: 70px; - margin: 5; - padding: 10; - align-items: center; - text-align: center; - font-size: 50; - border-style: solid; - border-width: 3; - border-color: black; - background: whitesmoke; - align-self: center; - border-radius: 10px; - } - - .list.active { - color: darkred; - border-color: darkred; - } -} \ No newline at end of file diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx deleted file mode 100644 index 4f37c45a8..000000000 --- a/src/mobile/MobileInterface.tsx +++ /dev/null @@ -1,871 +0,0 @@ -import { library } from '@fortawesome/fontawesome-svg-core'; -import { - faAddressCard, - faAlignLeft, - faAlignRight, - faAngleDoubleLeft, - faAngleRight, - faArrowDown, - faArrowLeft, - faArrowRight, - faArrowUp, - faArrowsAltH, - faAsterisk, - faBars, - faBell, - faBolt, - faBook, - faBrain, - faBullseye, - faCalculator, - faCamera, - faCaretDown, - faCaretLeft, - faCaretRight, - faCaretSquareDown, - faCaretSquareRight, - faCaretUp, - faCat, - faCheck, - faChevronLeft, - faChevronRight, - faClipboard, - faClone, - faCloudUploadAlt, - faCommentAlt, - faCompressArrowsAlt, - faCut, - faEdit, - faEllipsisV, - faEraser, - faExclamation, - faExpand, - faExternalLinkAlt, - faExternalLinkSquareAlt, - faEye, - faFileAlt, - faFileAudio, - faFileDownload, - faFilePdf, - faFilm, - faFilter, - faFolderOpen, - faFont, - faGlobeAsia, - faHandPointLeft, - faHighlighter, - faHome, - faImage, - faLocationArrow, - faLongArrowAltLeft, - faLongArrowAltRight, - faMicrophone, - faCircleHalfStroke, - faMinus, - faMobile, - faMousePointer, - faMusic, - faObjectGroup, - faPaintBrush, - faPalette, - faPause, - faPen, - faPenNib, - faPhone, - faPlay, - faPlus, - faPortrait, - faQuestionCircle, - faQuoteLeft, - faRedoAlt, - faReply, - faSearch, - faStamp, - faStickyNote, - faStop, - faTasks, - faTerminal, - faTh, - faThLarge, - faThumbtack, - faTimes, - faToggleOn, - faTrash, - faTrashAlt, - faTree, - faTv, - faUndoAlt, - faVideo, - faWindowClose, - faWindowMaximize, - faFile as fileSolid, -} from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../ClientUtils'; -import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes'; -import { Docs, DocumentOptions } from '../client/documents/Documents'; -import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; -import { SettingsManager } from '../client/util/SettingsManager'; -import { Transform } from '../client/util/Transform'; -import { UndoManager } from '../client/util/UndoManager'; -import { DashboardView } from '../client/views/DashboardView'; -import { GestureOverlay } from '../client/views/GestureOverlay'; -import { AudioBox } from '../client/views/nodes/AudioBox'; -import { DocumentView } from '../client/views/nodes/DocumentView'; -import { RadialMenu } from '../client/views/nodes/RadialMenu'; -import { RichTextMenu } from '../client/views/nodes/formattedText/RichTextMenu'; -import { Doc, DocListCast } from '../fields/Doc'; -import { InkTool } from '../fields/InkField'; -import { List } from '../fields/List'; -import { ScriptField } from '../fields/ScriptField'; -import { Cast, FieldValue, StrCast } from '../fields/Types'; -import './AudioUpload.scss'; -import { Uploader } from './ImageUpload'; -import './ImageUpload.scss'; -import './MobileInterface.scss'; -import { emptyFunction } from '../Utils'; - -library.add( - ...[ - faTasks, - faReply, - faQuoteLeft, - faHandPointLeft, - faFolderOpen, - faAngleDoubleLeft, - faExternalLinkSquareAlt, - faMobile, - faThLarge, - faWindowClose, - faEdit, - faTrashAlt, - faPalette, - faAngleRight, - faBell, - faTrash, - faCamera, - faExpand, - faCaretDown, - faCaretLeft, - faCaretRight, - faCaretSquareDown, - faCaretSquareRight, - faArrowsAltH, - faPlus, - faMinus, - faTerminal, - faToggleOn, - fileSolid, - faExternalLinkAlt, - faLocationArrow, - faSearch, - faFileDownload, - faStop, - faCalculator, - faWindowMaximize, - faAddressCard, - faQuestionCircle, - faArrowLeft, - faArrowRight, - faArrowDown, - faArrowUp, - faBolt, - faBullseye, - faCaretUp, - faCat, - faCheck, - faChevronRight, - faClipboard, - faClone, - faCloudUploadAlt, - faCommentAlt, - faCompressArrowsAlt, - faCut, - faEllipsisV, - faEraser, - faExclamation, - faFileAlt, - faFileAudio, - faFilePdf, - faFilm, - faFilter, - faFont, - faGlobeAsia, - faHighlighter, - faLongArrowAltRight, - faMicrophone, - faCircleHalfStroke, - faMousePointer, - faMusic, - faObjectGroup, - faPause, - faPen, - faPenNib, - faPhone, - faPlay, - faPortrait, - faRedoAlt, - faStamp, - faStickyNote, - faThumbtack, - faTree, - faTv, - faUndoAlt, - faBook, - faVideo, - faAsterisk, - faBrain, - faImage, - faPaintBrush, - faTimes, - faEye, - faHome, - faLongArrowAltLeft, - faBars, - faTh, - faChevronLeft, - faAlignLeft, - faAlignRight, - ].map(m => m as any) -); - -@observer -export class MobileInterface extends React.Component { - static Instance: MobileInterface; - private _library: Doc; - private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc()); - @observable private _sidebarActive: boolean = false; //to toggle sidebar display - @observable private _imageUploadActive: boolean = false; //to toggle image upload - @observable private _audioUploadActive: boolean = false; - @observable private _menuListView: boolean = false; //to switch between menu view (list / icon) - @observable private _ink: boolean = false; //toggle whether ink is being dispalyed - @observable private _homeMenu: boolean = true; // to determine whether currently at home menu - @observable private dashboards: Doc | null = null; // currently selected document - @observable private _activeDoc: Doc = this._mainDoc; // doc updated as the active mobile page is updated (initially home menu) - @observable private _homeDoc: Doc = this._mainDoc; // home menu as a document - @observable private _parents: Array = []; // array of parent docs (for pathbar) - - @computed private get mainContainer() { - return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : Doc.GuestMobile; - } - - constructor(props: Readonly<{}>) { - super(props); - this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), 'myDashboards'); // to access documents in Dash Web - MobileInterface.Instance = this; - } - - @action - componentDidMount() { - // if the home menu is in list view -> adjust the menu toggle appropriately - this._menuListView = this._homeDoc._type_collection === 'stacking' ? true : false; - Doc.ActiveTool = InkTool.None; // ink should intially be set to none - Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home - - // remove double click to avoid mobile zoom in - document.removeEventListener('dblclick', this.onReactDoubleClick); - document.addEventListener('dblclick', this.onReactDoubleClick); - } - - @action - componentWillUnmount = () => { - document.removeEventListener('dblclick', this.onReactDoubleClick); - }; - - // Prevent zooming in when double tapping the screen - onReactDoubleClick = (e: MouseEvent) => { - e.stopPropagation(); - }; - - // Switch the mobile view to the given doc - @action - switchCurrentView = (doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) => { - if (!Doc.UserDoc()) return; - if (this._activeDoc === this._homeDoc) { - this._parents.push(this._activeDoc); - this._homeMenu = false; - } - this._activeDoc = doc; - Doc.UserDoc().activeMobile = doc; - onSwitch?.(); - - // Ensures that switching to home is not registed - UndoManager.undoStack.length = 0; - UndoManager.redoStack.length = 0; - }; - - // For toggling the hamburger menu - @action - toggleSidebar = () => { - this._sidebarActive = !this._sidebarActive; - - if (this._ink) { - this.onSwitchInking(); - } - }; - /** - * Method called when 'Library' button is pressed on the home screen - */ - switchToLibrary = async () => { - this.switchCurrentView(this._library); - runInAction(() => (this._homeMenu = false)); - this.toggleSidebar(); - }; - - /** - * Back method for navigating through items - */ - @action - back = () => { - const header = document.getElementById('header') as HTMLElement; - const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document - // Case 1: Parent document is 'dashboards' - if (doc === (Cast(this._library, Doc) as Doc)) { - this.dashboards = null; - this.switchCurrentView(this._library); - // Case 2: Parent document is the 'home' menu (root node) - } else if (doc === (Cast(this._homeDoc, Doc) as Doc)) { - this._homeMenu = true; - this._parents = []; - this.dashboards = null; - this.switchCurrentView(this._homeDoc); - // Case 3: Parent document is any document - } else if (doc) { - this.dashboards = doc; - this.switchCurrentView(doc); - this._homeMenu = false; - header.textContent = String(doc.title); - } - this._ink = false; // turns ink off - }; - - /** - * Return 'Home", which implies returning to 'Home' menu buttons - */ - @action - returnHome = () => { - if (!this._homeMenu || this._sidebarActive) { - this._homeMenu = true; - this._parents = []; - this.dashboards = null; - this.switchCurrentView(this._homeDoc); - } - if (this._sidebarActive) { - this.toggleSidebar(); - } - }; - - /** - * Return to primary Dashboard in library (Dashboards Doc) - */ - @action - returnMain = () => { - this._parents = [this._homeDoc]; - this.switchCurrentView(this._library); - this._homeMenu = false; - this.dashboards = null; - }; - - /** - * Note: window.innerWidth and window.screen.width compute different values. - * window.screen.width is the display size, however window.innerWidth is the - * display resolution which computes differently. - */ - returnWidth = () => window.innerWidth; //The windows width - returnHeight = () => window.innerHeight - 300; //Calculating the windows height (-300 to account for topbar) - whitebackground = () => 'white'; - /** - * DocumentView for graphic display of all documents - */ - @computed get displayDashboards() { - return !this.mainContainer ? null : ( -
- -
- ); - } - - /** - * Handles the click functionality in the library panel. - * Navigates to the given doc and updates the sidebar. - * @param doc: doc for which the method is called - */ - handleClick = async (doc: Doc) => { - runInAction(() => { - if (doc.type !== 'collection' && this._sidebarActive) { - this._parents.push(this._activeDoc); - this.switchCurrentView(doc); - this._homeMenu = false; - this.toggleSidebar(); - } else { - this._parents.push(this._activeDoc); - this.switchCurrentView(doc); - this._homeMenu = false; - this.dashboards = doc; - } - }); - }; - - /** - * Called when an item in the library is clicked and should - * be opened (open icon on RHS of all menu items) - * @param doc doc to be opened - */ - @action - openFromSidebar = (doc: Doc) => { - this._parents.push(this._activeDoc); - this.switchCurrentView(doc); - this._homeMenu = false; - this.dashboards = doc; - this.toggleSidebar(); - }; - - // Renders the graphical pathbar - renderPathbar = () => { - const docPath = [...this._parents, this._activeDoc]; - const items = docPath.map((doc: Doc, index: any) => ( -
- {index === 0 ? null : } -
this.handlePathClick(doc, index)}> - {StrCast(doc.title)} -
-
- )); - return ( -
-
{items}
- {!this._parents.length ? null : ( -
- -
- )} -
-
- ); - }; - - // Handles when user clicks on a document in the pathbar - @action - handlePathClick = async (doc: Doc, index: number) => { - const library = await this._library; - if (doc === library) { - this.dashboards = null; - this.switchCurrentView(doc); - this._parents.length = index; - } else if (doc === this._homeDoc) { - this.returnHome(); - } else { - this.dashboards = doc; - this.switchCurrentView(doc); - this._parents.length = index; - } - }; - - // Renders the contents of the menu and sidebar - @computed get renderDefaultContent() { - if (this._homeMenu) { - return ( -
-
- - -
e.stopPropagation()}>
- -
- {this.renderPathbar()} -
- ); - } - // stores dashboards documents as 'dashboards' variable - let dashboards = Doc.MyDashboards; - if (this.dashboards) { - dashboards = this.dashboards; - } - // returns a list of navbar buttons as 'buttons' - const buttons = DocListCast(dashboards.data).map((doc: Doc, index: any) => { - if (doc.type !== 'ink') { - return ( -
-
this.handleClick(doc)}> - {' '} - {doc.title as string}{' '} -
-
this.handleClick(doc)}> - {doc.type as string} -
- this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === 'collection' ? 'block' : 'none'}` }} /> - this.openFromSidebar(doc)} icon="external-link-alt" size="lg" /> -
- ); - } - }); - - return ( -
-
- - -
- - - -
-
-
- {this.renderPathbar()} -
-
- {this.dashboards ? ( - <> - {buttons} -
- -
Return to dashboards
-
- - ) : ( - <> - {buttons} -
this.createNewDashboard()}> - -
Create New Dashboard
-
- - )} -
-
-
-
- ); - } - - /** - * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx) - */ - @action - createNewDashboard = (id?: string) => { - const scens = Doc.MyDashboards; - const dashboardCount = DocListCast(scens.data).length + 1; - const freeformOptions: DocumentOptions = { - x: 0, - y: 400, - title: 'Collection ' + dashboardCount, - }; - - const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = DashboardView.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row'); - - const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`); - dashboardDoc.contextMenuScripts = new List([toggleComic!, cloneDashboard!]); - dashboardDoc.contextMenuLabels = new List(['Toggle Comic Mode', 'New Dashboard Layout']); - - Doc.AddDocToList(scens, 'data', dashboardDoc); - }; - - // Button for switching between pen and ink mode - @action - onSwitchInking = () => { - const button = document.getElementById('inkButton') as HTMLElement; - button.style.backgroundColor = this._ink ? 'white' : 'black'; - button.style.color = this._ink ? 'black' : 'white'; - - if (!this._ink) { - Doc.ActiveTool = InkTool.Pen; - this._ink = true; - } else { - Doc.ActiveTool = InkTool.None; - this._ink = false; - } - }; - - // The static ink menu that appears at the top - @computed get inkMenu() { - return this._activeDoc._type_collection !== CollectionViewType.Docking || !this._ink ? null :
{/* */}
; - } - - // DocButton that uses UndoManager and handles the opacity change if CanUndo is true - @computed get undo() { - if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') { - return ( -
{ - UndoManager.Undo(); - e.stopPropagation(); - }}> - -
- ); - } else return null; - } - - // DocButton that uses UndoManager and handles the opacity change if CanRedo is true - @computed get redo() { - if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') { - return ( -
{ - UndoManager.Redo(); - e.stopPropagation(); - }}> - -
- ); - } else return null; - } - - // DocButton for switching into ink mode - @computed get drawInk() { - return !this.mainContainer || this._activeDoc._type_collection !== CollectionViewType.Docking ? null : ( -
- -
- ); - } - - // DocButton: Button that appears on the bottom of the screen to initiate image upload - @computed get uploadImageButton() { - if (this._activeDoc.type === DocumentType.COL && this._activeDoc !== this._homeDoc && this._activeDoc._type_collection !== CollectionViewType.Docking && this._activeDoc.title !== 'WORKSPACES') { - return ( -
- -
- ); - } else return null; - } - - // DocButton to download images on the mobile - @computed get downloadDocument() { - if (this._activeDoc.type === 'image' || this._activeDoc.type === 'pdf' || this._activeDoc.type === 'video') { - return ( -
window.open(this._activeDoc['data-path']?.toString())}> - {' '} - {/* daa-path holds the url */} - -
- ); - } else return null; - } - - // DocButton for pinning images to presentation - @computed get pinToPresentation() { - // Only making button available if it is an image - if (!(this._activeDoc.type === 'collection' || this._activeDoc.type === 'presentation')) { - return ( -
DocumentView.PinDoc(this._activeDoc, {})}> - -
- ); - } else return null; - } - - // Buttons for switching the menu between large and small icons - @computed get switchMenuView() { - return this._activeDoc.title !== this._homeDoc.title ? null : ( -
-
- -
-
- -
-
- ); - } - - // Logic for switching the menu into the icons - @action - changeToIconView = () => { - if ((this._homeDoc._type_collection = 'stacking')) { - this._menuListView = false; - this._homeDoc._type_collection = 'masonry'; - this._homeDoc.columnWidth = 300; - this._homeDoc._columnWidth = 300; - const menuButtons = DocListCast(this._homeDoc.data); - menuButtons.map(doc => { - const buttonData = DocListCast(doc.data); - buttonData[1]._nativeWidth = 0.1; - buttonData[1]._width = 0.1; - buttonData[1]._dimMagnitude = 0; - buttonData[1]._opacity = 0; - doc._nativeWidth = 400; - }); - } - }; - - // Logic for switching the menu into the stacking view - @action - changeToListView = () => { - if ((this._homeDoc._type_collection = 'masonry')) { - this._homeDoc._type_collection = 'stacking'; - this._menuListView = true; - const menuButtons = DocListCast(this._homeDoc.data); - menuButtons.map(doc => { - const buttonData = DocListCast(doc.data); - buttonData[1]._nativeWidth = 450; - buttonData[1]._dimMagnitude = 2; - buttonData[1]._opacity = 1; - doc._nativeWidth = 900; - }); - } - }; - - // For setting up the presentation document for the home menu - @action - setupDefaultPresentation = () => { - const presentation = Doc.ActivePresentation; - - if (presentation) { - this.switchCurrentView(presentation); - this._homeMenu = false; - } - }; - - // For toggling image upload pop up - @action - toggleUpload = () => (this._imageUploadActive = !this._imageUploadActive); - - // For toggling audio record and dictate pop up - @action - toggleAudio = () => (this._audioUploadActive = !this._audioUploadActive); - - // Button for toggling the upload pop up in a collection - @action - toggleUploadInCollection = () => { - const button = document.getElementById('imageButton') as HTMLElement; - button.style.backgroundColor = this._imageUploadActive ? 'white' : 'black'; - button.style.color = this._imageUploadActive ? 'black' : 'white'; - - this._imageUploadActive = !this._imageUploadActive; - }; - - // For closing the image upload pop up - @action - closeUpload = () => { - this._imageUploadActive = false; - }; - - // Returns the image upload pop up - @computed get uploadImage() { - const doc = !this._homeMenu ? this._activeDoc : (Cast(Doc.SharingDoc(), Doc) as Doc); - return ; - } - - // Radial menu can only be used if it is a colleciton and it is not a homeDoc - // (and cannot be used on Dashboard to avoid pin to presentation opening on right) - @computed get displayRadialMenu() { - return this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc._type_collection !== CollectionViewType.Docking ? : null; - } - - onDragOver = (e: React.DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - }; - - /** - * MENU BUTTON - * Switch view from mobile menu to access the mobile uploads - * Global function name: openMobileUploads() - */ - @action - switchToMobileUploads = () => { - const mobileUpload = Cast(Doc.SharingDoc(), Doc) as Doc; - this.switchCurrentView(mobileUpload); - this._homeMenu = false; - }; - - render() { - return ( -
- -
{this.uploadImage}
- {this.switchMenuView} - {this.inkMenu} - -
- -
-
- {this.pinToPresentation} - {this.downloadDocument} - {this.undo} - {this.redo} - {this.drawInk} - {this.uploadImageButton} -
- {this.displayDashboards} - {this.renderDefaultContent} -
- {this.displayRadialMenu} -
- ); - } -} - -//Global functions for mobile menu -ScriptingGlobals.add(function switchToMobileLibrary() { - return MobileInterface.Instance.switchToLibrary(); -}, 'opens the library to navigate through dashboards on Dash Mobile'); -ScriptingGlobals.add(function openMobileUploads() { - return MobileInterface.Instance.toggleUpload(); -}, 'opens the upload files menu for Dash Mobile'); -ScriptingGlobals.add(function switchToMobileUploadCollection() { - return MobileInterface.Instance.switchToMobileUploads(); -}, 'opens the mobile uploads collection on Dash Mobile'); -ScriptingGlobals.add(function openMobileAudio() { - return MobileInterface.Instance.toggleAudio(); -}, 'opens the record and dictate menu on Dash Mobile'); -ScriptingGlobals.add(function switchToMobilePresentation() { - return MobileInterface.Instance.setupDefaultPresentation(); -}, 'opens the presentation on Dash Mobile'); -ScriptingGlobals.add(function openMobileSettings() { - return SettingsManager.Instance.openMgr(); -}, 'opens settings on Dash Mobile'); - -// Other global functions for mobile -ScriptingGlobals.add( - function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { - return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); - }, - 'changes the active document displayed on the Dash Mobile', - '(doc: any)' -); diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx deleted file mode 100644 index 07839b6f6..000000000 --- a/src/mobile/MobileMain.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import { DocServer } from '../client/DocServer'; -import { Docs } from '../client/documents/Documents'; -import { CurrentUserUtils } from '../client/util/CurrentUserUtils'; -import { AssignAllExtensions } from '../extensions/Extensions'; -import { MobileInterface } from './MobileInterface'; - -AssignAllExtensions(); - -(async () => { - const info = await CurrentUserUtils.loadCurrentUser(); - DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)'); - await Docs.Prototypes.initialize(); - await CurrentUserUtils.loadUserDocument(info); - document.getElementById('root')!.addEventListener( - 'wheel', - event => { - if (event.ctrlKey) { - event.preventDefault(); - } - }, - true - ); - ReactDOM.render(, document.getElementById('root')); -})(); diff --git a/src/mobile/MobileMenu.scss b/src/mobile/MobileMenu.scss deleted file mode 100644 index 7f286efc4..000000000 --- a/src/mobile/MobileMenu.scss +++ /dev/null @@ -1,271 +0,0 @@ -$navbar-height: 120px; -$pathbar-height: 50px; - -* { - margin: 0px; - padding: 0px; - box-sizing: border-box; - font-family: "Open Sans"; -} - -body { - overflow: hidden; -} - -.navbar { - position: fixed; - top: 0px; - left: 0px; - width: 100vw; - height: $navbar-height; - background-color: whitesmoke; - border-bottom: 5px solid black; -} - -.navbar .toggle-btn { - position: absolute; - right: 20px; - top: 30px; - height: 70px; - width: 70px; - transition: all 300ms ease-in-out 200ms; -} - -.navbar .header { - position: absolute; - top: 50%; - top: calc(9px + 50%); - right: 50%; - transform: translate(50%, -50%); - font-size: 40; - user-select: none; - text-transform: uppercase; - font-family: Arial, Helvetica, sans-serif; -} - -.navbar .toggle-btn span { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 70%; - height: 4px; - background: black; - transition: all 200ms ease; -} - -.navbar .toggle-btn span:nth-child(1) { - transition: top 200ms ease-in-out; - top: 30%; -} - -.navbar .toggle-btn span:nth-child(3) { - transition: top 200ms ease-in-out; - top: 70%; -} - -.navbar .toggle-btn.active { - transition: transform 200ms ease-in-out 200ms; - transform: rotate(135deg); -} - -.navbar .toggle-btn.active span:nth-child(1) { - top: 50%; -} - -.navbar .toggle-btn.active span:nth-child(2) { - transform: translate(-50%, -50%) rotate(90deg); -} - -.navbar .toggle-btn.active span:nth-child(3) { - top: 50%; -} -// .navbar .home { -// position: relative; -// right: 5px; -// transform: translate(50%, -50%); -// font-size: 40; -// user-select: none; -// text-transform: uppercase; -// font-family: Arial, Helvetica, sans-serif; -// z-index: 200; -// } - -.sidebar { - position: absolute; - top: 200px; - opacity: 0; - right: -100%; - width: 100%; - height: calc(100% - (200px)); - z-index: 5; - background-color: whitesmoke; - transition: all 400ms ease 50ms; - padding: 20px; - // overflow-y: auto; - // -webkit-overflow-scrolling: touch; - - // border-right: 5px solid black; -} - -.sidebar .item { - width: 100%; - padding: 13px 12px; - border-bottom: 1px solid rgba(200, 200, 200, 0.7); - font-family: Arial, Helvetica, sans-serif; - font-style: normal; - font-weight: normal; - user-select: none; - font-size: 35px; - text-transform: uppercase; - color: black; - -} - -.sidebar .ink { - width: 100%; - padding: 13px 12px; - border-bottom: 1px solid rgba(200, 200, 200, 0.7); - font-family: Arial, Helvetica, sans-serif; - font-style: normal; - font-weight: normal; - user-select: none; - font-size: 35px; - text-transform: uppercase; - color: black; -} - -.sidebar .ink:focus { - outline: 1px solid blue; -} - -.sidebar .home { - position: absolute; - top: -135px; - right: calc(50% + 80px); - transform: translate(0%, -50%); - font-size: 40; - user-select: none; - text-transform: uppercase; - font-family: Arial, Helvetica, sans-serif; - z-index: 200; -} - -.type { - display: inline; - text-transform: lowercase; - margin-left: 20px; - font-size: 35px; - font-style: italic; - color: rgb(28, 28, 28); -} - -.right { - margin-left: 20px; - z-index: 200; -} - -.left { - width: 100%; - height: 100%; -} - -.sidebar .logout { - width: 100%; - padding: 13px 12px; - border-bottom: 1px solid rgba(200, 200, 200, 0.7); - font-family: Arial, Helvetica, sans-serif; - font-style: normal; - font-weight: normal; - user-select: none; - font-size: 30px; - text-transform: uppercase; - color: black; -} - -.sidebar .settings { - width: 100%; - padding: 13px 12px; - border-bottom: 1px solid rgba(200, 200, 200, 0.7); - font-family: Arial, Helvetica, sans-serif; - font-style: normal; - font-weight: normal; - user-select: none; - font-size: 30px; - text-transform: uppercase; - color: black; -} - - -.sidebar.active { - right: 0%; - opacity: 1; -} - -.back { - position: absolute; - top: -140px; - left: 50px; - transform: translate(0%, -50%); - color: black; - font-size: 60; - user-select: none; - text-transform: uppercase; - z-index: 100; - font-family: Arial, Helvetica, sans-serif; -} - - -.pathbar { - position: absolute; - top: 118px; - background: #1a1a1a; - z-index: 20; - border-radius: 0px; - width: 100%; - height: 80px; - transition: all 400ms ease 50ms; -} - -.pathname { - position: relative; - font-size: 25; - top: 50%; - width: 90%; - left: 3%; - color: whitesmoke; - transform: translate(0%, -50%); - z-index: 20; - font-family: sans-serif; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - text-align: left; - text-transform: uppercase; -} - -.homeContainer { - position: relative; - top: 200px; - height: calc(100% - 250px); - width: 90%; - overflow: scroll; - left: 5%; - background-color: lightpink; -} - -.pinButton { - position: relative; - width: 100px; - height: 100px; - font-size: 90px; - text-align: center; - left: 50%; - transform: translate(-50%, 0); - border-style: solid; - border-radius: 50px; - border-width: medium; - background-color: pink; - z-index: 100; -} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index 9c74bf24e..e1afc64e5 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -53,8 +53,6 @@ module.exports = { viewer: ['./src/debug/Viewer.tsx', 'webpack-hot-middleware/client?reload=true'], repl: ['./src/debug/Repl.tsx', 'webpack-hot-middleware/client?reload=true'], test: ['./src/debug/Test.tsx', 'webpack-hot-middleware/client?reload=true'], - inkControls: ['./src/mobile/InkControls.tsx', 'webpack-hot-middleware/client?reload=true'], - mobileInterface: ['./src/client/views/Main.tsx', 'webpack-hot-middleware/client?reload=true'], }, devtool: 'source-map', output: { @@ -119,6 +117,10 @@ module.exports = { { loader: 'sass-loader' }, ], }, + { + test: /\.(txt|d)$/i, + type: 'asset/source', + }, // -------- // SCSS MODULES - all have .module. in their name and can export to .tsx -- cgit v1.2.3-70-g09d2 From b6f6acb80f57011594d39b9ce576a5e77862cb7f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 15 Aug 2024 23:50:01 -0400 Subject: fixed horiz alignment of items in multicolumnview --- src/client/views/collections/CollectionStackingView.tsx | 9 +++++---- .../collectionMulticolumn/CollectionMulticolumnView.tsx | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 0f12c111e..3f8aee792 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -320,7 +320,7 @@ export class CollectionStackingView extends CollectionSubView this.getDocHeight(doc); const panelHeight = () => (this.isStackingView ? height() : Math.min(height(), this._props.PanelHeight())); - const panelWidth = () => (this.isStackingView ? width() : this.columnWidth); + const panelWidth = () => this.columnWidth; const stackedDocTransform = () => this.getDocTransform(doc); this._docXfs.push({ stackedDocTransform, width, height }); return count > this._renderCount ? null : ( @@ -373,9 +373,10 @@ export class CollectionStackingView extends CollectionSubView { - const { columnUnitLength } = this; - if (columnUnitLength === undefined) { + if (this.columnUnitLength === undefined) { return Transform.Identity(); // we're still waiting on promises to resolve } let offset = 0; // eslint-disable-next-line no-restricted-syntax for (const { layout: candidate } of this.childLayoutPairs) { if (candidate === layout) { - return this.ScreenToLocalBoxXf().translate(0, -offset / (this._props.NativeDimScaling?.() || 1)); + return this.ScreenToLocalBoxXf().translate(-offset / (this._props.NativeDimScaling?.() || 1), 0); } offset += this.lookupPixels(candidate) + resizerWidth; } -- cgit v1.2.3-70-g09d2 From 2e345c1ebd498e3f7e088e6fc3e1ca17082b23c1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 26 Aug 2024 13:53:26 -0400 Subject: converted unique faces to be a Doc type similar to a collection. --- src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 4 + src/client/views/DashboardView.tsx | 1 + src/client/views/Main.tsx | 3 +- .../views/collections/CollectionStackingView.tsx | 3 +- src/client/views/collections/CollectionSubView.tsx | 1 + .../collectionFreeForm/FaceCollectionBox.scss | 24 ++++- .../collectionFreeForm/FaceCollectionBox.tsx | 113 +++++++++++++-------- src/client/views/search/FaceRecognitionHandler.tsx | 34 +++++-- 9 files changed, 123 insertions(+), 61 deletions(-) (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 49df943d8..b055546fc 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -18,6 +18,7 @@ export enum DocumentType { SEARCH = 'search', // search query IMAGEGROUPER = 'imagegrouper', FACECOLLECTION = 'facecollection', + UFACE = 'uniqueface', // unique face collection doc LABEL = 'label', // simple text label BUTTON = 'button', // onClick button WEBCAM = 'webcam', // webcam diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c0e4e961c..c2211fb80 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -795,6 +795,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.FACECOLLECTION), undefined, options); } + export function UniqeFaceDocument(options: DocumentOptions = {}) { + return InstanceFromProto(Prototypes.get(DocumentType.UFACE), undefined, options); + } + export function LoadingDocument(file: File | string, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file === 'string' ? file : file.name, ...options }, undefined, ''); } diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 4616e15e5..33e905a54 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -432,6 +432,7 @@ export class DashboardView extends ObservableReactComponent { dashboardDoc.myOverlayDocs = new List(); dashboardDoc[DocData].myPublishedDocs = new List(); dashboardDoc[DocData].myTagCollections = new List(); + dashboardDoc[DocData].myUniqueFaces = new List(); dashboardDoc[DocData].myTrails = DashboardView.SetupDashboardTrails(dashboardDoc); dashboardDoc[DocData].myCalendars = DashboardView.SetupDashboardCalendars(dashboardDoc); // open this new dashboard diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx index 023324881..f7cd0e925 100644 --- a/src/client/views/Main.tsx +++ b/src/client/views/Main.tsx @@ -63,7 +63,7 @@ import { PresBox, PresElementBox } from './nodes/trails'; import { SearchBox } from './search/SearchBox'; import { ImageLabelBox } from './collections/collectionFreeForm/ImageLabelBox'; import { FaceRecognitionHandler } from './search/FaceRecognitionHandler'; -import { FaceCollectionBox } from './collections/collectionFreeForm/FaceCollectionBox'; +import { FaceCollectionBox, UniqueFaceBox } from './collections/collectionFreeForm/FaceCollectionBox'; import { Node } from 'prosemirror-model'; import { EditorView } from 'prosemirror-view'; @@ -140,6 +140,7 @@ FieldLoader.ServerLoadStatus = { requested: 0, retrieved: 0, message: 'cache' }; SearchBox, ImageLabelBox, FaceCollectionBox, + UniqueFaceBox, FunctionPlotBox, InkingStroke, LinkBox, diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 3f8aee792..6402ef16c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -98,7 +98,7 @@ export class CollectionStackingView extends CollectionSubView Doc | undefined; // specify a layout Doc template to use for children of the collection childHideDecorationTitle?: boolean; childHideResizeHandles?: boolean; + childHideDecorations?: boolean; childDragAction?: dropActionType; childXPadding?: number; childYPadding?: number; diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss index 480d109c8..86120f966 100644 --- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss +++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss @@ -1,10 +1,11 @@ .face-document-item { - background: #555555; margin-top: 10px; margin-bottom: 10px; padding: 10px; - border-radius: 10px; + border-radius: inherit; position: relative; + width: 100%; + height: 100%; h1 { color: white; @@ -14,14 +15,31 @@ .face-collection-buttons { position: absolute; - top: 10px; + top: 0px; right: 10px; } + .face-collection-toggle { + position: absolute; + top: 0px; + left: 10px; + } + .face-document-top { + position: absolute; + top: 0; + margin: auto; + width: 100%; + left: 0; + } .face-document-image-container { display: flex; justify-content: center; flex-wrap: wrap; + overflow-x: hidden; + overflow-y: auto; + position: absolute; + top: 75px; + height: calc(100% - 75px); .image-wrapper { position: relative; diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx index 1cc1f59dc..6a0d51e32 100644 --- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx +++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx @@ -6,23 +6,21 @@ import 'ldrs/ring'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; -import { setupMoveUpEvents } from '../../../../ClientUtils'; -import { Utils, emptyFunction } from '../../../../Utils'; +import { lightOrDark, returnTrue, setupMoveUpEvents } from '../../../../ClientUtils'; +import { emptyFunction } from '../../../../Utils'; import { Doc, Opt } from '../../../../fields/Doc'; -import { Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; -import { ImageCast } from '../../../../fields/Types'; +import { ImageCast, StrCast } from '../../../../fields/Types'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; import { DragManager } from '../../../util/DragManager'; import { dropActionType } from '../../../util/DropActionTypes'; -import { SnappingManager } from '../../../util/SnappingManager'; import { undoable } from '../../../util/UndoManager'; import { ViewBoxBaseComponent } from '../../DocComponent'; -import { ObservableReactComponent } from '../../ObservableReactComponent'; import { DocumentView } from '../../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { FaceRecognitionHandler } from '../../search/FaceRecognitionHandler'; +import { CollectionStackingView } from '../CollectionStackingView'; import './FaceCollectionBox.scss'; import { MarqueeOptionsMenu } from './MarqueeOptionsMenu'; @@ -31,31 +29,29 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu'; * unique face in turn displays the set of images that correspond to the face. */ -interface UniqueFaceProps { - faceDoc: Doc; -} - /** - * React component for rendering a unique face and its collection of image Docs. + * Viewer for unique face Doc collections. * * This both displays a collection of images corresponding tp a unique face, and * allows for editing the face collection by removing an image, or drag-and-dropping * an image that was not recognized. */ @observer -export class UniqueFaceView extends ObservableReactComponent { +export class UniqueFaceBox extends ViewBoxBaseComponent() { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(UniqueFaceBox, fieldKey); + } private _dropDisposer?: DragManager.DragDropDisposer; + private _oldWheel: HTMLElement | null = null; - constructor(props: UniqueFaceProps) { + constructor(props: FieldViewProps) { super(props); makeObservable(this); } - @observable _displayImages: boolean = true; - protected createDropTarget = (ele: HTMLDivElement) => { this._dropDisposer?.(); - ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this._props.faceDoc)); + ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this._props.Document)); }; protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { @@ -63,12 +59,12 @@ export class UniqueFaceView extends ObservableReactComponent { ?.filter(doc => doc.type === DocumentType.IMG) .forEach(imgDoc => { // If the current Face Document has no faces, and the doc has more than one face descriptor, don't let the user add the document first. Or should we just use the first face ? - if (FaceRecognitionHandler.UniqueFaceDescriptors(this._props.faceDoc).length === 0 && FaceRecognitionHandler.ImageDocFaceDescriptors(imgDoc).length > 1) { + if (FaceRecognitionHandler.UniqueFaceDescriptors(this._props.Document).length === 0 && FaceRecognitionHandler.ImageDocFaceDescriptors(imgDoc).length > 1) { alert('Cannot add a document with multiple faces as the first item!'); } else { // Loop through the documents' face descriptors and choose the face in the iage with the smallest distance (most similar to the face colleciton) - const faceDescriptorsAsFloat32Array = FaceRecognitionHandler.UniqueFaceDescriptors(this._props.faceDoc).map(fd => new Float32Array(Array.from(fd))); - const labeledFaceDescriptor = new faceapi.LabeledFaceDescriptors(FaceRecognitionHandler.UniqueFaceLabel(this._props.faceDoc), faceDescriptorsAsFloat32Array); + const faceDescriptorsAsFloat32Array = FaceRecognitionHandler.UniqueFaceDescriptors(this._props.Document).map(fd => new Float32Array(Array.from(fd))); + const labeledFaceDescriptor = new faceapi.LabeledFaceDescriptors(FaceRecognitionHandler.UniqueFaceLabel(this._props.Document), faceDescriptorsAsFloat32Array); const faceMatcher = new FaceMatcher([labeledFaceDescriptor], 1); const { face_match } = FaceRecognitionHandler.ImageDocFaceDescriptors(imgDoc).reduce( (prev, face) => { @@ -80,11 +76,12 @@ export class UniqueFaceView extends ObservableReactComponent { // assign the face in the image that's closest to the face collection to be the face that's assigned to the collection if (face_match) { - FaceRecognitionHandler.UniqueFaceAddFaceImage(imgDoc, face_match, this._props.faceDoc); + FaceRecognitionHandler.UniqueFaceAddFaceImage(imgDoc, face_match, this._props.Document); } } }); - return false; + e.stopPropagation(); + return true; } /** @@ -92,14 +89,15 @@ export class UniqueFaceView extends ObservableReactComponent { */ @action onDisplayClick() { - this._displayImages = !this._displayImages; + this._props.Document._displayImages = !this.props.Document._displayImages; + this._props.Document.height = this._props.Document._displayImages ? 400 : 100; } /** * Removes a unique face Doc from the colelction of unique faces. */ deleteUniqueFace = undoable(() => { - FaceRecognitionHandler.DeleteUniqueFace(this._props.faceDoc); + FaceRecognitionHandler.DeleteUniqueFace(this._props.Document); }, 'delete face'); /** @@ -107,9 +105,11 @@ export class UniqueFaceView extends ObservableReactComponent { * @param imgDoc - image Doc to remove */ removeFaceImageFromUniqueFace = undoable((imgDoc: Doc) => { - FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, this._props.faceDoc); + FaceRecognitionHandler.UniqueFaceRemoveFaceImage(imgDoc, this._props.Document); }, 'remove doc from face'); + onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); + render() { return (
this.createDropTarget(ele!)}> @@ -117,23 +117,35 @@ export class UniqueFaceView extends ObservableReactComponent {
-

{FaceRecognitionHandler.UniqueFaceLabel(this._props.faceDoc)}

+

{FaceRecognitionHandler.UniqueFaceLabel(this._props.Document)}

- this.onDisplayClick()} - icon={} - color={MarqueeOptionsMenu.Instance.userColor} - style={{ width: '19px' }} - /> - {this._displayImages ? ( -
- {FaceRecognitionHandler.UniqueFaceImages(this._props.faceDoc).map(doc => { +
+ this.onDisplayClick()} + icon={} + color={MarqueeOptionsMenu.Instance.userColor} + style={{ width: '19px' }} + /> +
+ {this.props.Document._displayImages ? ( +
{ + this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); + this._oldWheel = ele; + // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling + ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); + }}> + {FaceRecognitionHandler.UniqueFaceImages(this._props.Document).map((doc, i) => { const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.'); return (
setupMoveUpEvents( this, @@ -165,8 +177,8 @@ export class UniqueFaceView extends ObservableReactComponent { * * Since the collection of recognized faces is stored on the active dashboard, this class * does not itself store any Docs, but accesses the myUniqueFaces field of the current - * dashboard. Each Face collection Doc is rendered using a FaceCollectionDocView which - * is not a CollectionView or even a DocumentView (but probably should be). + * dashboard. (This should probably go away as Doc type in favor of it just being a + * stacking collection of uniqueFace docs) */ @observer export class FaceCollectionBox extends ViewBoxBaseComponent() { @@ -179,18 +191,29 @@ export class FaceCollectionBox extends ViewBoxBaseComponent() { makeObservable(this); } + moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => !!(this._props.removeDocument?.(doc) && addDocument?.(doc)); + render() { - return ( -
- {FaceRecognitionHandler.UniqueFaces().map(doc => ( - - ))} -
+ return !Doc.ActiveDashboard ? null : ( + ); } } Docs.Prototypes.TemplateMap.set(DocumentType.FACECOLLECTION, { layout: { view: FaceCollectionBox, dataField: 'data' }, - options: { acl: '', _width: 400 }, + options: { acl: '', _width: 400, dropAction: dropActionType.embed }, +}); + +Docs.Prototypes.TemplateMap.set(DocumentType.UFACE, { + layout: { view: UniqueFaceBox, dataField: 'face_images' }, + options: { acl: '', _width: 400, _height: 400 }, }); diff --git a/src/client/views/search/FaceRecognitionHandler.tsx b/src/client/views/search/FaceRecognitionHandler.tsx index a17e4c54a..1ab084eaa 100644 --- a/src/client/views/search/FaceRecognitionHandler.tsx +++ b/src/client/views/search/FaceRecognitionHandler.tsx @@ -8,6 +8,8 @@ import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { ImageField } from '../../../fields/URLField'; +import { UniqueFaceBox } from '../collections/collectionFreeForm/FaceCollectionBox'; +import { Docs } from '../../documents/Documents'; /** * A singleton class that handles face recognition and manages face Doc collections for each face found. @@ -169,15 +171,25 @@ export class FaceRecognitionHandler { * @returns a unique face Doc */ private createUniqueFaceDoc = (dashboard: Doc) => { - const faceDocNum = NumCast(dashboard.myUniqueFaces_count) + 1; - dashboard.myUniqueFaces_count = faceDocNum; // TODO: improve to a better name - - const uniqueFaceDoc = new Doc(); - uniqueFaceDoc.title = `Face ${faceDocNum}`; - uniqueFaceDoc.face = ''; // just to make prettyprinting look better - uniqueFaceDoc.face_label = `Face${faceDocNum}`; - uniqueFaceDoc.face_images = new List(); - uniqueFaceDoc.face_descriptors = new List>(); + const faceDocNum = NumCast(dashboard[DocData].myUniqueFaces_count) + 1; + dashboard[DocData].myUniqueFaces_count = faceDocNum; // TODO: improve to a better name + + const uniqueFaceDoc = Docs.Create.UniqeFaceDocument({ + title: `Face ${faceDocNum}`, + _layout_reflowHorizontal: true, + _layout_reflowVertical: true, + _layout_nativeDimEditable: true, + _layout_borderRounding: '20px', + backgroundColor: '#555555', + _width: 400, + _height: 400, + }); + const uface = uniqueFaceDoc[DocData]; + uface.face = ''; // just to make prettyprinting look better + uface.face_label = `Face${faceDocNum}`; + uface.face_images = new List(); + uface.face_descriptors = new List>(); + Doc.SetContainer(uniqueFaceDoc, Doc.MyFaceCollection); Doc.ActiveDashboard && Doc.AddDocToList(Doc.ActiveDashboard[DocData], 'myUniqueFaces', uniqueFaceDoc); return uniqueFaceDoc; @@ -222,8 +234,8 @@ export class FaceRecognitionHandler { setTimeout(() => this.classifyFacesInImage(imgDoc), 1000); } else { const imgUrl = ImageCast(imgDoc[Doc.LayoutFieldKey(imgDoc)]); - if (imgUrl && !DocListCast(Doc.MyFaceCollection.examinedFaceDocs).includes(imgDoc)) { // only examine Docs that have an image and that haven't already been examined. - Doc.AddDocToList(Doc.MyFaceCollection, 'examinedFaceDocs', imgDoc); + if (imgUrl && !DocListCast(Doc.MyFaceCollection.examinedFaceDocs).includes(imgDoc[DocData])) { // only examine Docs that have an image and that haven't already been examined. + Doc.AddDocToList(Doc.MyFaceCollection, 'examinedFaceDocs', imgDoc[DocData]); FaceRecognitionHandler.initImageDocFaceDescriptors(imgDoc); FaceRecognitionHandler.loadImage(imgUrl).then( // load image and analyze faces img => faceapi.detectAllFaces(img).withFaceLandmarks().withFaceDescriptors() -- cgit v1.2.3-70-g09d2 From b9f15c10e4cfa1288e176cbd1d312c628c5998ad Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 10 Sep 2024 14:54:44 -0400 Subject: moved TagsView up into DocumentView so that one row of tags will be visible. fixed setting pixel size of images to resize annotations so they don't appear to change. added vert/horiz centering for stacking views. fixed pres box to not have scroll bars. fixed resizing properties panel. --- src/client/documents/Documents.ts | 3 ++- src/client/util/CurrentUserUtils.ts | 12 ++++++----- src/client/views/MainView.tsx | 8 +++++--- src/client/views/SidebarAnnos.tsx | 1 + src/client/views/StyleProvider.tsx | 2 +- src/client/views/TagsView.tsx | 7 +++++-- .../views/collections/CollectionCalendarView.tsx | 1 + .../views/collections/CollectionStackingView.scss | 7 ++----- .../views/collections/CollectionStackingView.tsx | 13 +++++++++--- .../CollectionStackingViewFieldColumn.tsx | 24 ++++++++++------------ src/client/views/collections/TreeView.tsx | 2 +- .../collectionFreeForm/FaceCollectionBox.tsx | 1 + src/client/views/global/globalScripts.ts | 14 ++++++++----- src/client/views/nodes/DocumentView.scss | 2 +- src/client/views/nodes/FieldView.tsx | 1 + src/client/views/nodes/ImageBox.tsx | 14 +++++++++++-- 16 files changed, 70 insertions(+), 42 deletions(-) (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d5a7b0465..fbdf361dd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -260,6 +260,7 @@ export class DocumentOptions { _layout_noSidebar?: BOOLt = new BoolInfo('whether to display the sidebar toggle button'); layout_boxShadow?: string; // box-shadow css string OR "standard" to use dash standard box shadow layout_maxShown?: NUMt = new NumInfo('maximum number of children to display at one time (see multicolumnview)'); + _layout_dontCenter?: STRt = new StrInfo("whether collections will center their content - values of 'x', 'xy', or 'y'"); _layout_autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents'); _layout_autoHeightMargins?: NUMt = new NumInfo('Margin heights to be added to the computed auto height of a Doc'); _layout_curPage?: NUMt = new NumInfo('current page of a PDF or other? paginated document', false); @@ -979,7 +980,7 @@ export namespace Docs { } export function StackingDocument(documents: Array, options: DocumentOptions, id?: string, protoId?: string) { - return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { ...options, _type_collection: CollectionViewType.Stacking }, id, undefined, protoId); + return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _layout_dontCenter: 'y', ...options, _type_collection: CollectionViewType.Stacking }, id, undefined, protoId); } export function NoteTakingDocument(documents: Array, options: DocumentOptions, id?: string, protoId?: string) { diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f042f33ce..2014c48e1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -368,7 +368,7 @@ pie title Minerals in my tap water {key: "Chat", creator: Docs.Create.ChatDocument, opts: { _width: 300, _height: 300, }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 120, _header_pointerEvents: "all", _header_height: 50, _header_fontSize: 9,_layout_autoHeightMargins: 50, _layout_autoHeight: true, treeView_HideUnrendered: true}}, {key: "ViewSlide", creator: slideView, opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}}, - {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }}, + {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, _layout_dontCenter:'xy', dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }}, {key: "Tab", creator: opts => Docs.Create.FreeformDocument([], opts), opts: { _width: 500, _height: 800, _layout_fitWidth: true, _freeform_backgroundGrid: true, }}, {key: "Slide", creator: opts => Docs.Create.TreeDocument([], opts), opts: { _width: 300, _height: 200, _type_collection: CollectionViewType.Tree, treeView_HasOverlay: true, _text_fontSize: "20px", _layout_autoHeight: true, @@ -477,6 +477,7 @@ pie title Minerals in my tap water const reqdStackOpts:DocumentOptions ={ title: "menuItemPanel", childDragAction: dropActionType.same, layout_boxShadow: "rgba(0,0,0,0)", dontRegisterView: true, ignoreClick: true, + _layout_dontCenter: 'y', _chromeHidden: true, _gridGap: 0, _yMargin: 0, _xMargin: 0, _layout_autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, isSystem: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" }); @@ -510,7 +511,7 @@ pie title Minerals in my tap water // doc.myUserBtns = new PrefetchProxy(userBtns); const reqdToolOps:DocumentOptions = { title: "My Tools", isSystem: true, ignoreClick: true, layout_boxShadow: "0 0", - layout_explainer: "This is a palette of documents that can be created.", + layout_explainer: "This is a palette of documents that can be created.", _layout_dontCenter: "y", _layout_showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _dragOnlyWithinContainer: true, layout_hideContextMenu: true, _chromeHidden: true, }; return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, userBtns]); @@ -663,7 +664,8 @@ pie title Minerals in my tap water } static stackTools(): Button[] { return [ - { title: "Center", icon: "align-center", toolTip: "Center Align Stack", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"center", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "V. Align", icon: "pallet", toolTip: "Center Align Stack", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"vcenter", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform + { title: "Center", icon: "align-center", toolTip: "Center Align Stack", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"hcenter", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform ] } static cardTools(): Button[] { @@ -777,8 +779,8 @@ pie title Minerals in my tap water } static imageTools() { return [ - { title: "Pixels",toolTip: "Set Native Pixel Sizze", btnType: ButtonType.ClickButton, icon: "portrait", scripts: { onClick: 'imageSetPixelSize();' }}, - { title: "Rotate",toolTip: "Rotate 90", btnType: ButtonType.ClickButton, icon: "redo-alt", scripts: { onClick: 'imageRotate90();' }}, + { title: "Pixels",toolTip: "Set Native Pixel Size", btnType: ButtonType.ClickButton, icon: "portrait", scripts: { onClick: 'imageSetPixelSize();' }}, + { title: "Rotate",toolTip: "Rotate 90", btnType: ButtonType.ClickButton, icon: "redo-alt", scripts: { onClick: 'imageRotate90();' }}, ]; } static contextMenuTools():Button[] { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 9f1c7da3d..393abea53 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons'; import * as far from '@fortawesome/free-regular-svg-icons'; import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, configure, makeObservable, observable, reaction, runInAction } from 'mobx'; +import { action, computed, configure, makeObservable, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ResizeObserver from 'resize-observer-polyfill'; @@ -707,8 +707,8 @@ export class MainView extends ObservableReactComponent { setupMoveUpEvents( this, e, - action(() => { - SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - e.clientX)); + action(moveEv => { + SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - moveEv.clientX)); return !SnappingManager.PropertiesWidth; }), action(() => { @@ -807,6 +807,7 @@ export class MainView extends ObservableReactComponent { childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} + dontCenter="y" /> ); @@ -832,6 +833,7 @@ export class MainView extends ObservableReactComponent { }; @computed get mainInnerContent() { + trace(); const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); const width = this.propertiesWidth() + leftMenuFlyoutWidth; return ( diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 8f0a35df0..dd60bfa65 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -246,6 +246,7 @@ export class SidebarAnnos extends ObservableReactComponent, props: Opt ); }; - const tags = () => props?.DocumentView?.() && CollectionFreeFormDocumentView.from(props.DocumentView()) ? : null; + const tags = () => props?.DocumentView?.() ? : null; return ( <> {paint()} diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx index 65e70e8fc..8047363d9 100644 --- a/src/client/views/TagsView.tsx +++ b/src/client/views/TagsView.tsx @@ -91,7 +91,7 @@ export class TagItem extends ObservableReactComponent { const tagCollection = TagItem.findTagCollectionDoc(tag) ?? TagItem.createTagCollectionDoc(tag); // If the document is of type COLLECTION, make it a smart collection, otherwise, add the tag to the document. - if (doc.type === DocumentType.COL) { + if (doc.type === DocumentType.COL && !doc.annotationOn) { Doc.AddDocToList(tagCollection[DocData], 'collections', doc); // Iterate through the tag Doc collections and add a copy of the document to each collection @@ -257,6 +257,7 @@ export class TagsView extends ObservableReactComponent { super(props); makeObservable(this); } + InsetDist = 25; // how far tag panel is moved up to overlap DocumentView. @observable _panelHeightDirty = 0; @observable _currentInput = ''; @@ -328,7 +329,7 @@ export class TagsView extends ObservableReactComponent { return !this._props.View.Document.showTags ? null : (
r && new ResizeObserver(action(() => (this._props.View.TagPanelHeight = r?.getBoundingClientRect().height ?? 0))).observe(r)} + ref={r => r && new ResizeObserver(action(() => (this._props.View.TagPanelHeight = Math.max(0, (r?.getBoundingClientRect().height ?? 0) - this.InsetDist)))).observe(r)} style={{ transformOrigin: 'top left', maxWidth: `${100 * this.currentScale}%`, @@ -336,6 +337,8 @@ export class TagsView extends ObservableReactComponent { transform: `scale(${1 / this.currentScale})`, backgroundColor: this.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT, borderColor: this.isEditing ? Colors.BLACK : Colors.TRANSPARENT, + position: 'relative', + top: `calc(-${this.InsetDist} * ${1 / this.currentScale}px)`, }}>
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx index 30fd9fc2b..0ea9f8ebc 100644 --- a/src/client/views/collections/CollectionCalendarView.tsx +++ b/src/client/views/collections/CollectionCalendarView.tsx @@ -82,6 +82,7 @@ export class CollectionCalendarView extends CollectionSubView() { isAnnotationOverlay={false} // select={emptyFunction} What does this mean? isAnyChildContentActive={returnTrue} // ?? + dontCenter="y" // childDocumentsActive={} // whenChildContentsActiveChanged={} childHideDecorationTitle={false} diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index 6225cc52a..6400a0a8e 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -54,11 +54,8 @@ } .collectionStackingViewFieldColumn { - height: max-content; - } - - .collectionStackingViewFieldColumnDragging { - height: 100%; + display: flex; + flex-direction: column; } .collectionSchemaView-previewDoc { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 6402ef16c..2cfe9329a 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -146,7 +146,7 @@ export class CollectionStackingView extends CollectionSubView @@ -343,7 +343,7 @@ export class CollectionStackingView extends CollectionSubView Array.from(this.Sections); // what a section looks like if we're in stacking view sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { @@ -565,6 +571,7 @@ export class CollectionStackingView extends CollectionSubView ); }; diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index 5ae08e535..aba6b51d4 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -41,6 +41,7 @@ interface CSVFieldColumnProps { columnWidth: number; numGroupColumns: number; gridGap: number; + dontCenter: 'x' | 'xy' | 'y'; type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined; headings: () => object[]; // I think that stacking view actually has a single column and then supposedly you can add more columns? Unsure @@ -345,15 +346,6 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent< - {/* {evContents === noValueHeader ? null : ( -
- - - -
- )} */}
{this._props.Document._columnsHideIfEmpty ? null : headingView} {this.collapsed ? null : ( -
+
headings.indexOf(i) === idx); return (
{ }; docTransform = () => this.refTransform(this._dref?.ContentDiv); getTransform = () => this.refTransform(this._tref.current); - embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1); + embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1) - 3 /* paddingRight for bullet */; embeddedPanelHeight = () => { const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.Document))(this.treeView._props.childLayoutTemplate?.()) || this.layoutDoc; return Math.min( diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx index 717081666..f9f6c81ab 100644 --- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx +++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx @@ -264,6 +264,7 @@ export class FaceCollectionBox extends ViewBoxBaseComponent() { isContentActive={returnTrue} isAnyChildContentActive={returnTrue} childHideDecorations={true} + dontCenter="y" />
); diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index 2c7920bdd..934ed6c3e 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -133,10 +133,10 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { }); // eslint-disable-next-line prefer-arrow-callback -ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) { +ScriptingGlobals.add(function showFreeform(attr: 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) { const selected = DocumentView.SelectedDocs().lastElement(); // prettier-ignore - const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4', + const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4', { waitForRender?: boolean; checkResult: (doc: Doc) => boolean; @@ -158,9 +158,13 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' else (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce(); }, }], - ['center', { - checkResult: (doc:Doc) => BoolCast(doc?._stacking_alignCenter, false), - setDoc: (doc:Doc) => { doc._stacking_alignCenter = !doc._stacking_alignCenter; }, + ['vcenter', { + checkResult: (doc:Doc) => !StrCast(doc?._layout_dontCenter).includes('y'), + setDoc: (doc:Doc) => { doc._layout_dontCenter = StrCast(doc.layout_dontCenter).includes('y') ? StrCast(doc.layout_dontCenter).replace(/y/,"") : StrCast(doc.layout_dontCenter) + 'y'; }, + }], + ['hcenter', { + checkResult: (doc:Doc) => !StrCast(doc?._layout_dontCenter).includes('x'), + setDoc: (doc:Doc) => { doc._layout_dontCenter = StrCast(doc.layout_dontCenter).includes('x') ? StrCast(doc.layout_dontCenter).replace(/x/,"") : 'x'+ StrCast(doc.layout_dontCenter); }, }], ['clusters', { waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 23dada260..2873b4bba 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -242,7 +242,7 @@ .contentFittingDocumentView { position: relative; - display: flex; + display: block; width: 100%; height: 100%; transition: inherit; diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index dd71fd946..c269c7bcb 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -82,6 +82,7 @@ export interface FieldViewSharedProps { // eslint-disable-next-line no-use-before-define onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined; fitWidth?: (doc: Doc) => boolean | undefined; + dontCenter?: 'x' | 'y' | 'xy' | undefined; searchFilterDocs: () => Doc[]; showTitle?: () => string; whenChildContentsActiveChanged: (isActive: boolean) => void; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d0a7fc6ac..be3525544 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -11,7 +11,7 @@ import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { ObjectField } from '../../../fields/ObjectField'; -import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, ImageCast, NumCast, RTFCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction } from '../../../Utils'; @@ -188,8 +188,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { @undoBatch setNativeSize = action(() => { + const oldnativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']); const nscale = NumCast(this._props.PanelWidth()) * NumCast(this.layoutDoc._freeform_scale, 1); - const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']); + const nw = nscale / oldnativeWidth; this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nw; this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nw; this.dataDoc._freeform_panX = nw * NumCast(this.dataDoc._freeform_panX); @@ -198,6 +199,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent() { this.dataDoc._freeform_panX_min = this.dataDoc._freeform_panX_min ? nw * NumCast(this.dataDoc._freeform_panX_min) : undefined; this.dataDoc._freeform_panY_max = this.dataDoc._freeform_panY_max ? nw * NumCast(this.dataDoc._freeform_panY_max) : undefined; this.dataDoc._freeform_panY_min = this.dataDoc._freeform_panY_min ? nw * NumCast(this.dataDoc._freeform_panY_min) : undefined; + const newnativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']); + DocListCast(this.dataDoc[this.annotationKey]).forEach(doc => { + doc.x = (NumCast(doc.x) / oldnativeWidth) * newnativeWidth; + doc.y = (NumCast(doc.y) / oldnativeWidth) * newnativeWidth; + if (!RTFCast(doc[Doc.LayoutFieldKey(doc)])) { + doc.width = (NumCast(doc.width) / oldnativeWidth) * newnativeWidth; + doc.height = (NumCast(doc.height) / oldnativeWidth) * newnativeWidth; + } + }); }); @undoBatch rotate = action(() => { -- cgit v1.2.3-70-g09d2 From dc81c08086446df700185a0ad0a8ee8f16e06ab7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 11 Sep 2024 09:35:10 -0400 Subject: fixed stacking view to used computedFn's to avoid invalidations and resotred DocumentView to flex layout by fixing stacking view's css --- .../views/collections/CollectionStackingView.tsx | 51 ++++++++++------------ src/client/views/nodes/DocumentView.scss | 2 +- 2 files changed, 24 insertions(+), 29 deletions(-) (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 2cfe9329a..e97ee713e 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -35,6 +35,7 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; +import { computedFn } from 'mobx-utils'; export type collectionStackingViewProps = { sortFunc?: (a: Doc, b: Doc) => number; @@ -127,6 +128,9 @@ export class CollectionStackingView extends CollectionSubView this.columnWidth; + columnDocHeightFn = (doc: Doc) => () => (this.isStackingView ? this.getDocHeight(doc)() : Math.min(this.getDocHeight(doc)(), this._props.PanelHeight())); + // TODO: plj - these are the children children = (docs: Doc[]) => { // TODO: can somebody explain me to what exactly TraceMobX is? @@ -140,17 +144,14 @@ export class CollectionStackingView extends CollectionSubView { - const height = () => this.getDocHeight(d); - const width = () => this.getDocWidth(d); - const trans = () => this.getDocTransition(d); // assuming we need to get rowSpan because we might be dealing with many columns. Grid gap makes sense if multiple columns - const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap); + const rowSpan = Math.ceil((this.getDocHeight(d)() + this.gridGap) / this.gridGap); // just getting the style - const style = this.isStackingView ? { margin: this.dontCenter.includes('x') ? undefined : 'auto', transition: trans(), width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` }; + const style = this.isStackingView ? { margin: undefined, transition: this.getDocTransition(d)(), width: this.columnWidth, marginTop: i ? this.gridGap : 0, height: this.getDocHeight(d)() } : { gridRowEnd: `span ${rowSpan}` }; // So we're choosing whether we're going to render a column or a masonry doc return (
- {this.getDisplayDoc(d, width, trans, i)} + {this.getDisplayDoc(d, this.getDocTransition(d), i)}
); }); @@ -311,26 +312,23 @@ export class CollectionStackingView extends CollectionSubView (this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false ? false : undefined); @observable docRefs = new ObservableMap(); childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null)); // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list - getDisplayDoc(doc: Doc, width: () => number, trans: () => string, count: number) { + getDisplayDoc(doc: Doc, trans: () => string, count: number) { const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined; - const height = () => this.getDocHeight(doc); - const panelHeight = () => (this.isStackingView ? height() : Math.min(height(), this._props.PanelHeight())); - const panelWidth = () => this.columnWidth; - const stackedDocTransform = () => this.getDocTransform(doc); - this._docXfs.push({ stackedDocTransform, width, height }); + this._docXfs.push({ stackedDocTransform: this.getDocTransform(doc), width: this.getDocWidth(doc), height: this.getDocHeight(doc) }); return count > this._renderCount ? null : ( r?.ContentDiv && this.docRefs.set(doc, r))} Document={doc} TemplateDataDocument={dataDoc} renderDepth={this._props.renderDepth + 1} - PanelWidth={panelWidth} - PanelHeight={panelHeight} + PanelWidth={this.columnWidthFn} + PanelHeight={this.columnDocHeightFn(doc)} pointerEvents={this.DocumentView?.()._props.onClickScript?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView) styleProvider={this.styleProvider} containerViewPath={this.childContainerViewPath} @@ -341,8 +339,8 @@ export class CollectionStackingView extends CollectionSubView () => { // these must be referenced for document decorations to update when the text box container is scrolled this._scroll; this._props.ScreenToLocalTransform(); @@ -381,8 +379,8 @@ export class CollectionStackingView extends CollectionSubView () => { if (!d) return 0; const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.()); const maxWidth = this.columnWidth / this.numGroupColumns; @@ -390,12 +388,9 @@ export class CollectionStackingView extends CollectionSubView () => StrCast(d?.dataTransition)); + getDocHeight = computedFn((d?: Doc) => () => { if (!d || d.hidden) return 0; const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.()); const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument; @@ -404,13 +399,13 @@ export class CollectionStackingView extends CollectionSubView { diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 2873b4bba..23dada260 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -242,7 +242,7 @@ .contentFittingDocumentView { position: relative; - display: block; + display: flex; width: 100%; height: 100%; transition: inherit; -- cgit v1.2.3-70-g09d2 From d95730d904612640184ca6fdc00864b0c81b0c0c Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 18 Sep 2024 20:46:38 -0400 Subject: lots of changes to fix dragging cards, integrate iconTags with other tags, sizing docs when selected to fit window, --- eslint.config.mjs | 1 + src/client/util/CurrentUserUtils.ts | 14 +- src/client/util/DragManager.ts | 3 +- src/client/views/DocumentButtonBar.tsx | 44 ++---- src/client/views/DocumentDecorations.tsx | 4 +- src/client/views/StyleProvider.tsx | 4 - src/client/views/TagsView.tsx | 20 +-- .../views/collections/CollectionCardDeckView.scss | 8 +- .../views/collections/CollectionCardDeckView.tsx | 152 ++++++++++++--------- .../views/collections/CollectionCarouselView.tsx | 1 - .../views/collections/CollectionStackingView.tsx | 3 - src/client/views/collections/CollectionSubView.tsx | 4 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 5 + src/client/views/nodes/IconTagBox.scss | 24 ++-- src/client/views/nodes/IconTagBox.tsx | 106 +++++++------- .../views/nodes/formattedText/FormattedTextBox.tsx | 1 + src/client/views/search/FaceRecognitionHandler.tsx | 3 +- 18 files changed, 198 insertions(+), 204 deletions(-) (limited to 'src/client/views/collections/CollectionStackingView.tsx') diff --git a/eslint.config.mjs b/eslint.config.mjs index 8926afd7c..f7063caa5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -36,6 +36,7 @@ export default [ 'no-multi-assign': 'off', 'no-underscore-dangle': 'off', 'no-nested-ternary': 'off', + 'no-param-reassign': 'error', 'lines-between-class-members': 'off', 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'warn', diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index eee6be937..262916312 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,7 +2,7 @@ import { reaction, runInAction } from "mobx"; import * as rp from 'request-promise'; import { ClientUtils, OmitKeys } from "../../ClientUtils"; -import { Doc, DocListCast, DocListCastAsync, FieldType, Opt } from "../../fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, FieldType, Opt, StrListCast } from "../../fields/Doc"; import { DocData } from "../../fields/DocSymbols"; import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; @@ -688,18 +688,18 @@ pie title Minerals in my tap water static tagGroupTools(): Button[] { if (!Doc.UserDoc().activeDashboard) { - Doc.UserDoc().myFilterHotKeyTitles = new List(['Star', 'Heart', 'Bolt', 'Cloud']); + Doc.UserDoc().myFilterHotKeyTitles = new List(['star', 'heart', 'bolt', 'cloud']); - ['Star', 'Heart', 'Bolt', 'Cloud'].forEach(key => { + StrListCast(Doc.UserDoc().myFilterHotKeyTitles).forEach(key => { Doc.UserDoc()[key] = key.toLowerCase(); }); } // hack: if there's no dashboard, create default filters. otherwise, just make sure that the Options button is preserved return (Doc.UserDoc().activeDashboard ? [] : [ - { title: "Star", isSystem: false, icon: "star", toolTip:"Click to toggle the star group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, - { title: "Heart", isSystem: false,icon: "heart", toolTip:"Click to toggle the heart group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, - { title: "Bolt", isSystem: false,icon: "bolt", toolTip:"Click to toggle the bolt group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, - { title: "Cloud", isSystem: false,icon: "cloud", toolTip:"Click to toggle the cloud group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, + { title: "star", isSystem: false,icon: "star", toolTip:"Click to toggle the star group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, + { title: "heart", isSystem: false,icon: "heart", toolTip:"Click to toggle the heart group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"heart", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, + { title: "bolt", isSystem: false,icon: "bolt", toolTip:"Click to toggle the bolt group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"bolt", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, + { title: "cloud", isSystem: false,icon: "cloud", toolTip:"Click to toggle the cloud group's visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"cloud", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}}, ]).concat([ { title: "Options", isSystem: true,icon: "gear", toolTip:"Click to customize your filter panel", btnType: ButtonType.ClickButton, expertMode: false, toolType:"opts", funcs: {}, scripts: { onClick: '{ return handleTags(this.toolType, _readOnly_);}'}} ]) diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index d55d193cc..81ea840f1 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -29,7 +29,7 @@ import { dropActionType } from './DropActionTypes'; import { SnappingManager } from './SnappingManager'; import { UndoManager } from './UndoManager'; -// eslint-disable-next-line @typescript-eslint/no-var-requires +// eslint-disable-next-line @typescript-eslint/no-require-imports const { contextMenuZindex } = require('../views/global/globalCssVariables.module.scss'); // prettier-ignore /** @@ -101,7 +101,6 @@ export namespace DragManager { // event called when the drag operation results in a drop action export class DropEvent { - // eslint-disable-next-line no-useless-constructor constructor( readonly x: number, readonly y: number, diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 87dd5f45a..32bf67df1 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -9,7 +9,7 @@ import * as React from 'react'; import { FaEdit } from 'react-icons/fa'; import { returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; -import { Doc } from '../../fields/Doc'; +import { Doc, DocListCast } from '../../fields/Doc'; import { Cast, DocCast } from '../../fields/Types'; import { DocUtils, IsFollowLinkScript } from '../documents/DocUtils'; import { CalendarManager } from '../util/CalendarManager'; @@ -28,7 +28,6 @@ import { DocumentLinksButton } from './nodes/DocumentLinksButton'; import { DocumentView } from './nodes/DocumentView'; import { OpenWhere } from './nodes/OpenWhere'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; -import { DocData } from '../../fields/DocSymbols'; @observer export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (DocumentView | undefined)[]; stack?: unknown }> { @@ -287,44 +286,27 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( get keywordButton() { const targetDoc = this.view0?.Document; - const metaBtn = (name: string, icon: IconProp) => { - const tooltip = `Toggle ${name}`; - return ( - {tooltip}
}> -
- { - if (name === 'tags') { - targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags); - } else { - targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels); - } - }} - /> -
- - ); - }; - return !targetDoc ? null : (
-
+ {/*
{metaBtn('tags', 'star')} {metaBtn('keywords', 'id-card')} -
+
*/} Open keyword menu
}>
{ - // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags; - }}> + onClick={undoable(e => { + const showing = DocumentView.Selected().some(dv => dv.showTags); + DocumentView.Selected().forEach(dv => { + dv.layoutDoc._layout_showTags = !showing; + if (e.shiftKey) + DocListCast(dv.Document[Doc.LayoutFieldKey(dv.Document) + '_annotations']).forEach(doc => { + if (doc.face) doc.hidden = showing; + }); + }); + }, 'show Doc tags')}>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 5e7908725..2ba96c2a9 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -837,7 +837,7 @@ export class DocumentDecorations extends ObservableReactComponent 1 ? 0 : `${seldocview.Document._layout_showTags ? 4 + seldocview.TagPanelHeight : 4}px`, + top: DocumentView.Selected().length > 1 ? 0 : `${seldocview.showTags ? 4 + seldocview.TagPanelHeight : 4}px`, transform: `translate(${-this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `, }}> DocumentView.Selected()} /> @@ -846,7 +846,7 @@ export class DocumentDecorations extends ObservableReactComponent {DocumentView.Selected().length > 1 ? : null} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index eb434db40..3545afcee 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -9,7 +9,6 @@ import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs'; import { FaFilter } from 'react-icons/fa'; import { ClientUtils, DashColor, lightOrDark } from '../../ClientUtils'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; -import { DocData } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { ScriptField } from '../../fields/ScriptField'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../fields/Types'; @@ -22,7 +21,6 @@ import { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; import { DocumentView, DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; -import { IconTagBox } from './nodes/IconTagBox'; import { StyleProp } from './StyleProp'; import './StyleProvider.scss'; import { TagsView } from './TagsView'; @@ -366,7 +364,6 @@ export function DefaultStyleProvider(doc: Opt, props: Opt docView?.() ? : null; - const iconTags = () => doc?.[DocData].showIconTags ? : null; return ( <> @@ -375,7 +372,6 @@ export function DefaultStyleProvider(doc: Opt, props: Opt ); } diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx index be2c28185..a678d5580 100644 --- a/src/client/views/TagsView.tsx +++ b/src/client/views/TagsView.tsx @@ -18,6 +18,7 @@ import { ObservableReactComponent } from './ObservableReactComponent'; import './TagsView.scss'; import { DocumentView } from './nodes/DocumentView'; import { FaceRecognitionHandler } from './search/FaceRecognitionHandler'; +import { IconTagBox } from './nodes/IconTagBox'; /** * The TagsView is a metadata input/display panel shown at the bottom of a DocumentView in a freeform collection. @@ -59,7 +60,7 @@ export class TagItem extends ObservableReactComponent { * @param tag tag string * @returns tag collection Doc or undefined */ - public static findTagCollectionDoc = (tag: String) => TagItem.AllTagCollectionDocs.find(doc => doc.title === tag); + public static findTagCollectionDoc = (tag: string) => TagItem.AllTagCollectionDocs.find(doc => doc.title === tag); /** * Creates a Doc that collects Docs with the specified tag / value @@ -148,7 +149,7 @@ export class TagItem extends ObservableReactComponent { private _ref: React.RefObject; - constructor(props: any) { + constructor(props: TagItemProps) { super(props); makeObservable(this); this._ref = React.createRef(); @@ -225,7 +226,7 @@ export class TagItem extends ObservableReactComponent { type="checkbox" onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()} - onChange={undoable(e => (this.doc[metadata] = !this.doc[metadata]), 'metadata toggle')} + onChange={undoable(() => (this.doc[metadata] = !this.doc[metadata]), 'metadata toggle')} checked={this.doc[metadata] as boolean} /> ) : ( @@ -257,7 +258,7 @@ interface TagViewProps { */ @observer export class TagsView extends ObservableReactComponent { - constructor(props: any) { + constructor(props: TagViewProps) { super(props); makeObservable(this); } @@ -271,7 +272,7 @@ export class TagsView extends ObservableReactComponent { componentDidMount() { this._heightDisposer = reaction( () => this.View.screenToContentsTransform(), - xf => { + () => { this._panelHeightDirty = this._panelHeightDirty + 1; } ); @@ -288,7 +289,7 @@ export class TagsView extends ObservableReactComponent { return this._props.Views.length > 1 ? 1 : Math.max(1, 1 / this.View.screenToLocalScale()); } @computed get isEditing() { - return this._isEditing && (this._props.Views.length > 1 || DocumentView.SelectedDocs().includes(this.View.Document)); + return this._isEditing && (this._props.Views.length > 1 || (DocumentView.Selected().length === 1 && DocumentView.Selected().includes(this.View))); } /** @@ -336,7 +337,7 @@ export class TagsView extends ObservableReactComponent { ); this._panelHeightDirty; - return this.View.ComponentView?.isUnstyledView?.() || (!this.View.Document._layout_showTags && this._props.Views.length === 1) ? null : ( + return this.View.ComponentView?.isUnstyledView?.() || (!this.View.showTags && this._props.Views.length === 1) ? null : (
r && new ResizeObserver(action(() => this._props.Views.length === 1 && (this.View.TagPanelHeight = Math.max(0, (r?.getBoundingClientRect().height ?? 0) - this.InsetDist)))).observe(r)} @@ -352,9 +353,10 @@ export class TagsView extends ObservableReactComponent { }}>
- {!tagsList.size && !facesList.size ? null : ( // + {this._props.Views.length === 1 && !this.View.showTags ? null : ( // this.setToEditing(!this._isEditing)} icon={} /> )} + {Array.from(tagsList).map((tag, i) => ( { />
- {TagItem.AllTagCollectionDocs.map((doc, i) => { + {TagItem.AllTagCollectionDocs.map(doc => { const tag = StrCast(doc.title); return ( - - ))} -
- ); - }; /** * Opens the filter panel in the properties menu @@ -93,13 +50,13 @@ export class IconTagBox extends ObservableReactComponent { }; /** - * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt - * @param childPairIndex * @param buttonID * @param doc */ - toggleButton = undoable((doc: Doc, icon: string) => { - BoolCast(doc[icon]) ? (doc[icon] = false) : (doc[icon] = true); + setIconTag = undoable((icon: string, state: boolean) => { + this._props.Views.forEach(view => { + view.dataDoc[icon] = state; + }); }, 'toggle card tag'); /** @@ -112,10 +69,43 @@ export class IconTagBox extends ObservableReactComponent { const isActive = doc[icon.toString()]; const color = isActive ? '#4476f7' : '#323232'; - return ; + return ; }; + /** + * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups + */ render() { - return <>{this.renderButtons(this._props.doc)}; + const amButtons = StrListCast(Doc.UserDoc().myFilterHotKeyTitles).length + 1; + + const keys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); + + const iconMap = (buttonID: number) => { + return StrCast(Doc.UserDoc()[keys[buttonID]]) as IconProp; + }; + const buttons = numberRange(amButtons - 1) + .filter(i => this._props.IsEditing || this.View.Document[iconMap(i).toString()] || (DocumentView.Selected.length === 1 && this.View.IsSelected)) + .map(i => ( + Click to add/remove this card from the {iconMap(i).toString()} group
}> + + + )); + return !buttons.length ? null : ( +
+ {buttons} +
+ ); } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 343e255dc..e0331a422 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -275,6 +275,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent 1; - imgDocFaceDescriptions.forEach((fd, i) => { + imgDocFaceDescriptions.forEach(fd => { const faceDescriptor = new List(Array.from(fd.descriptor)); const matchedUniqueFace = this.findMatchingFaceDoc(fd.descriptor) ?? this.createUniqueFaceDoc(activeDashboard); const faceAnno = Docs.Create.FreeformDocument([], { -- cgit v1.2.3-70-g09d2