diff options
Diffstat (limited to 'src')
71 files changed, 630 insertions, 585 deletions
diff --git a/src/client/util/CalendarManager.tsx b/src/client/util/CalendarManager.tsx index d0cd69273..d28b3a2c9 100644 --- a/src/client/util/CalendarManager.tsx +++ b/src/client/util/CalendarManager.tsx @@ -2,7 +2,7 @@ import { DateRangePicker, Provider, defaultTheme } from '@adobe/react-spectrum'; import { IconLookup, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { TextField } from '@mui/material'; -import { Button } from 'browndash-components'; +import { Button } from '@dash/components'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx index 9d0817a06..1ec85c9d9 100644 --- a/src/client/util/GroupManager.tsx +++ b/src/client/util/GroupManager.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Size, Type } from 'browndash-components'; +import { Button, IconButton, Size, Type } from '@dash/components'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/util/GroupMemberView.tsx b/src/client/util/GroupMemberView.tsx index 88d73d742..cfeaf02d7 100644 --- a/src/client/util/GroupMemberView.tsx +++ b/src/client/util/GroupMemberView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Size, Type } from 'browndash-components'; +import { Button, IconButton, Size, Type } from '@dash/components'; import { action, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index 628097ca8..5d041f7b4 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, ColorPicker, Colors, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { Button, ColorPicker, Colors, Dropdown, DropdownType, EditableText, Group, NumberDropdown, Size, Toggle, ToggleType, Type } from '@dash/components'; import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 117d7935e..efc8e79a6 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Size, Type } from 'browndash-components'; +import { Button, IconButton, Size, Type } from '@dash/components'; import { concat, intersection } from 'lodash'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/util/reportManager/ReportManager.tsx b/src/client/util/reportManager/ReportManager.tsx index c969f9036..a6b5911f7 100644 --- a/src/client/util/reportManager/ReportManager.tsx +++ b/src/client/util/reportManager/ReportManager.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-unused-class-component-methods */ import { Octokit } from '@octokit/core'; -import { Button, Dropdown, DropdownType, IconButton, Type } from 'browndash-components'; +import { Button, Dropdown, DropdownType, IconButton, Type } from '@dash/components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss index 90f64b393..25feca7bf 100644 --- a/src/client/views/DashboardView.scss +++ b/src/client/views/DashboardView.scss @@ -1,31 +1,39 @@ @import './global/globalCssVariables.module'; +$dashboard-left-menu-width: 250px; +$dashboard-view-padding: 20px; +$dashboard-container-height: 200px; +$dashboard-container-width: 250px; + .dashboard-view { - padding: 50px; display: flex; flex-direction: row; width: 100%; - position: absolute; height: 100%; - width: 100%; - padding-right: 0px; + position: absolute; overflow: auto; .left-menu { display: flex; justify-content: flex-start; flex-direction: column; - width: 250px; - min-width: 250px; + position: fixed; + min-width: $dashboard-left-menu-width; gap: 5px; + padding: $dashboard-view-padding; } .all-dashboards { display: flex; flex-direction: row; flex-wrap: wrap; - overflow-y: auto; width: 100%; + height: fit-content; + justify-content: flex-start; + align-items: flex-start; + padding: $dashboard-view-padding 0px 0px $dashboard-left-menu-width; + gap: 10px; + margin-bottom: 60px; } } @@ -48,13 +56,12 @@ .dashboard-container-new { border-radius: 10px; - width: 250px; - height: 200px; + width: $dashboard-container-width; + height: $dashboard-container-height; font-size: 120px; font-weight: 100; text-align: center; border: solid 2px $light-gray; - margin: 0 0px 30px 30px; cursor: pointer; color: $light-gray; display: flex; @@ -82,12 +89,12 @@ border-radius: 10px; position: relative; cursor: pointer; - width: 250px; - height: 200px; + width: $dashboard-container-width; + height: $dashboard-container-height; outline: solid 2px $light-gray; + outline-offset: -2px; display: flex; flex-direction: column; - margin: 0 0px 30px 30px; overflow: hidden; &:hover { diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 448178397..7f0118ed3 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, ColorPicker, EditableText, Size, Type } from 'browndash-components'; +import { Button, ColorPicker, EditableText, Size, Type } from '@dash/components'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index d722b28b5..a9f03a658 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -2,7 +2,7 @@ import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCalendarDays } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { Popup } from 'browndash-components'; +import { Popup } from '@dash/components'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 867ecdf35..8ab85bfaf 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { IconButton } from 'browndash-components'; +import { IconButton } from '@dash/components'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -560,7 +560,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora } } if (['bottom', 'top'].includes(opts.dragHdl) && modifyNativeDim && Doc.NativeHeight(doc)) { - const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight; + const setData = Doc.NativeHeight(doc[DocData]) === doc.nativeHeight && (!doc.layout_reflowVertical || opts.ctrlKey); doc._nativeHeight = scale.y * Doc.NativeHeight(doc); if (setData) Doc.SetNativeHeight(doc[DocData], NumCast(doc._nativeHeight)); } diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index ce6b26323..e3df01bbb 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -1,7 +1,7 @@ /* eslint-disable no-use-before-define */ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Toggle, ToggleType, Type } from 'browndash-components'; +import { Toggle, ToggleType, Type } from '@dash/components'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cd6b97255..7abca5197 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -7,7 +7,7 @@ import { action, computed, configure, makeObservable, observable, reaction, runI 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 '@dash/components/src/global/globalCssVariables.scss'; import { ClientUtils, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents } from '../../ClientUtils'; import { emptyFunction } from '../../Utils'; import { Doc, DocListCast, GetDocFromUrl, Opt, returnEmptyDoclist } from '../../fields/Doc'; diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx index 4a35805fb..178eefe2c 100644 --- a/src/client/views/MainViewModal.tsx +++ b/src/client/views/MainViewModal.tsx @@ -1,5 +1,5 @@ /* eslint-disable react/require-default-props */ -import { isDark } from 'browndash-components'; +import { isDark } from '@dash/components'; import { observer } from 'mobx-react'; import * as React from 'react'; import { SnappingManager } from '../util/SnappingManager'; diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index fa1123a2d..02516264c 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -15,18 +15,19 @@ import './MarqueeAnnotator.scss'; import { DocumentView } from './nodes/DocumentView'; import { ObservableReactComponent } from './ObservableReactComponent'; import { AnchorMenu } from './pdf/AnchorMenu'; +import { Transform } from '../util/Transform'; export interface MarqueeAnnotatorProps { Document: Doc; down?: number[]; scrollTop: number; - isNativeScaled?: boolean; scaling?: () => number; annotationLayerScaling?: () => number; annotationLayerScrollTop: number; containerOffset?: () => number[]; marqueeContainer: HTMLDivElement; docView: () => DocumentView; + screenTransform: () => Transform; savedAnnotations: () => ObservableMap<number, (HTMLDivElement & { marqueeing?: boolean })[]>; selectionText: () => string; annotationLayer: HTMLDivElement; @@ -157,7 +158,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP // 4) reattach the vector to the center of the bounding box getTransformedScreenPt = (down: number[]) => { const { marqueeContainer } = this.props; - const containerXf = this.props.isNativeScaled ? this.props.docView().screenToContentsTransform() : this.props.docView().screenToViewTransform(); + const containerXf = this.props.screenTransform(); const boundingRect = marqueeContainer.getBoundingClientRect(); const center = { x: boundingRect.x + boundingRect.width / 2, y: boundingRect.y + boundingRect.height / 2 }; const downVec = Utils.rotPt(down[0] - center.x, diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index f96a4a255..606fb17ed 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/no-unused-class-component-methods */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from 'browndash-components'; +import { Dropdown, DropdownType, IListItemProps, Toggle, ToggleType, Type } from '@dash/components'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 5b24eb7ea..ae8abea2c 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -2,7 +2,8 @@ import { IconLookup, IconProp } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@mui/material'; -import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Toggle, ToggleType, Type } from 'browndash-components'; +import { Colors, EditableText, IconButton, NumberInput, Size, Slider, Toggle, ToggleType, Type } from '@dash/components'; +import { Property } from 'csstype'; import { concat } from 'lodash'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 0a9da1237..e825a27d3 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { Dropdown, DropdownType, IconButton, IListItemProps, Shadows, Size, Type } from 'browndash-components'; +import { Dropdown, DropdownType, IconButton, IListItemProps, Shadows, Size, Type } from '@dash/components'; import { action, untracked } from 'mobx'; import { extname } from 'path'; import * as React from 'react'; diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx index 2615bc5fb..21b6a76c7 100644 --- a/src/client/views/TagsView.tsx +++ b/src/client/views/TagsView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, Colors, IconButton } from 'browndash-components'; +import { Button, Colors, IconButton } from '@dash/components'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; diff --git a/src/client/views/UndoStack.tsx b/src/client/views/UndoStack.tsx index 9b71d46ea..32b97b31a 100644 --- a/src/client/views/UndoStack.tsx +++ b/src/client/views/UndoStack.tsx @@ -1,5 +1,5 @@ import { Tooltip } from '@mui/material'; -import { Popup, Type } from 'browndash-components'; +import { Popup, Type } from '@dash/components'; import { observer } from 'mobx-react'; import * as React from 'react'; import { StrCast } from '../../fields/Types'; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 3e8138390..539b49c86 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -382,7 +382,7 @@ export class CollectionDockingView extends CollectionSubView() { } catch { /* empty */ } - this._goldenLayout?.destroy(); + setTimeout(() => this._goldenLayout?.destroy()); window.removeEventListener('resize', this.onResize); window.removeEventListener('mouseup', this.onPointerUp); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index dab1298d5..de999c91a 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -2,7 +2,7 @@ /* eslint-disable react/sort-comp */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { Toggle, ToggleType, Type } from 'browndash-components'; +import { Toggle, ToggleType, Type } from '@dash/components'; import { Lambda, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx index c298bff22..77f1db9ad 100644 --- a/src/client/views/collections/FlashcardPracticeUI.tsx +++ b/src/client/views/collections/FlashcardPracticeUI.tsx @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { MultiToggle, Type } from 'browndash-components'; +import { MultiToggle, Type } from '@dash/components'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 60728877a..cc56a8ff9 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Popup, Type } from 'browndash-components'; +import { Popup, Type } from '@dash/components'; import { clamp } from 'lodash'; import { IReactionDisposer, ObservableSet, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 9284a36a2..ab4d8b060 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton, Size } from 'browndash-components'; +import { IconButton, Size } from '@dash/components'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index 51add85a8..437888ef2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -1,4 +1,4 @@ -import { IconButton, Size, Type } from 'browndash-components'; +import { IconButton, Size, Type } from '@dash/components'; import { IReactionDisposer, action, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index af82724ef..63e5e33ca 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,5 +1,9 @@ import { Bezier } from 'bezier-js'; +<<<<<<< HEAD import { Button, Colors, Type } from 'browndash-components'; +======= +import { Colors } from '@dash/components'; +>>>>>>> 2f7d1f0073943e1eb9e0f34c4459bc0176377697 import { Property } from 'csstype'; import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx index 6d51ecac6..b9f8b13a7 100644 --- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx +++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton, Size } from 'browndash-components'; +import { IconButton, Size } from '@dash/components'; import * as faceapi from 'face-api.js'; import { FaceMatcher } from 'face-api.js'; import 'ldrs/ring'; diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx index 583f2e656..a3d9641da 100644 --- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx +++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Colors, IconButton } from 'browndash-components'; +import { Colors, IconButton } from '@dash/components'; import similarity from 'compute-cosine-similarity'; import { ring } from 'ldrs'; import 'ldrs/ring'; diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx index 73befb205..f050b9846 100644 --- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx +++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton } from 'browndash-components'; +import { IconButton } from '@dash/components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx index de65b240f..abd828945 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton } from 'browndash-components'; +import { IconButton } from '@dash/components'; import { computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index ceae43c04..80116dd2f 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { Toggle, ToggleType, Type } from 'browndash-components'; +import { Toggle, ToggleType, Type } from '@dash/components'; import { Property } from 'csstype'; import { IReactionDisposer, action, makeObservable, reaction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index d67e10c0b..8aae24df0 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; -import { Button, IconButton } from 'browndash-components'; +import { Button, IconButton } from '@dash/components'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index aef97e723..5a5cc3622 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton, Size } from 'browndash-components'; +import { IconButton, Size } from '@dash/components'; import { IReactionDisposer, Lambda, ObservableMap, action, computed, makeObservable, observable, observe, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx index 9ffdd812f..81a2d8e64 100644 --- a/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx +++ b/src/client/views/collections/collectionSchema/SchemaColumnHeader.tsx @@ -17,7 +17,7 @@ import { DocCast } from '../../../../fields/Types'; import { computedFn } from 'mobx-utils'; import { CollectionSchemaView } from './CollectionSchemaView'; import { undoable } from '../../../util/UndoManager'; -import { IconButton, Size } from 'browndash-components'; +import { IconButton, Size } from '@dash/components'; export enum SchemaFieldType { Header, @@ -122,45 +122,53 @@ export class SchemaColumnHeader extends ObservableReactComponent<SchemaColumnHea @computed get editableView() { const { color, fieldProps, pointerEvents } = this.renderProps(this._props); - return <div className='schema-column-edit-wrapper' onPointerUp={() => { - SchemaColumnHeader.isDefaultField(this.fieldKey) && this.openKeyDropdown(); - this._props.schemaView.deselectAllCells(); - }} - style={{ - color, - width: '100%', - pointerEvents, - }}> - <EditableView - ref={r => {this._inputRef = r; this._props.autoFocus && r?.setIsFocused(true)}} - oneLine={true} - allowCRs={false} - contents={''} - onClick={this.openKeyDropdown} - fieldContents={fieldProps} - editing={undefined} - placeholder={'Add key'} - updateAlt={this.updateAlt} // alternate title to display - updateSearch={this.updateKeyDropdown} - inputString={true} - inputStringPlaceholder={'Add key'} - GetValue={() => { - if (SchemaColumnHeader.isDefaultField(this.fieldKey)) return ''; - else if (this._altTitle) return this._altTitle; - else return this.fieldKey; + return ( + <div + className="schema-column-edit-wrapper" + onPointerUp={() => { + SchemaColumnHeader.isDefaultField(this.fieldKey) && this.openKeyDropdown(); + this._props.schemaView.deselectAllCells(); }} - SetValue={undoable((value: string, shiftKey?: boolean, enterKey?: boolean) => { - if (enterKey) { - // if shift & enter, set value of each cell in column - this.setColumnValues(value, ''); - this._altTitle = undefined; + style={{ + color, + width: '100%', + pointerEvents, + }}> + <EditableView + ref={r => { + this._inputRef = r; + this._props.autoFocus && r?.setIsFocused(true); + }} + oneLine={true} + allowCRs={false} + contents={''} + onClick={this.openKeyDropdown} + fieldContents={fieldProps} + editing={undefined} + placeholder={'Add key'} + updateAlt={this.updateAlt} // alternate title to display + updateSearch={this.updateKeyDropdown} + inputString={true} + inputStringPlaceholder={'Add key'} + GetValue={() => { + if (SchemaColumnHeader.isDefaultField(this.fieldKey)) return ''; + else if (this._altTitle) return this._altTitle; + else return this.fieldKey; + }} + SetValue={undoable((value: string, shiftKey?: boolean, enterKey?: boolean) => { + if (enterKey) { + // if shift & enter, set value of each cell in column + this.setColumnValues(value, ''); + this._altTitle = undefined; + this._props.finishEdit?.(); + return true; + } this._props.finishEdit?.(); return true; - } - this._props.finishEdit?.(); - return true; - }, 'edit column header')}/> + }, 'edit column header')} + /> </div> + ); } public static isDefaultField = (key: string) => { diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx index 7f519b065..16f8b86f3 100644 --- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx +++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx @@ -1,4 +1,4 @@ -import { IconButton, Size } from 'browndash-components'; +import { IconButton, Size } from '@dash/components'; import { computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx index f036ff843..cd46ae824 100644 --- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx +++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-use-before-define */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Popup, Size, Type } from 'browndash-components'; +import { Popup, Size, Type } from '@dash/components'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import { extname } from 'path'; diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index c632d6001..1738802b7 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Colors } from 'browndash-components'; +import { Colors } from '@dash/components'; import { runInAction } from 'mobx'; import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; diff --git a/src/client/views/newlightbox/Header/LightboxHeader.tsx b/src/client/views/newlightbox/Header/LightboxHeader.tsx index 882d28fba..64bafd9aa 100644 --- a/src/client/views/newlightbox/Header/LightboxHeader.tsx +++ b/src/client/views/newlightbox/Header/LightboxHeader.tsx @@ -1,4 +1,4 @@ -import { Button, IconButton, Size, Type } from 'browndash-components'; +import { Button, IconButton, Size, Type } from '@dash/components'; import * as React from 'react'; import { BsBookmark, BsBookmarkFill } from 'react-icons/bs'; import { MdTravelExplore } from 'react-icons/md'; diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx index 27413bac3..7660da1f5 100644 --- a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx @@ -1,6 +1,6 @@ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable guard-for-in */ -import { IconButton, Size, Type } from 'browndash-components'; +import { IconButton, Size, Type } from '@dash/components'; import * as React from 'react'; import { FaCaretDown, FaCaretUp } from 'react-icons/fa'; import { GrClose } from 'react-icons/gr'; diff --git a/src/client/views/newlightbox/components/EditableText/EditableText.tsx b/src/client/views/newlightbox/components/EditableText/EditableText.tsx index 6273e1859..cff84e990 100644 --- a/src/client/views/newlightbox/components/EditableText/EditableText.tsx +++ b/src/client/views/newlightbox/components/EditableText/EditableText.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import './EditableText.scss'; -import { Size } from 'browndash-components'; +import { Size } from '@dash/components'; export interface IEditableTextProps { text: string; diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 3905403ea..3cbfb1796 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,4 @@ -import { Colors } from 'browndash-components'; +import { Colors } from '@dash/components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 7925410e2..b874d077b 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox } from '@mui/material'; -import { Colors, Toggle, ToggleType, Type } from 'browndash-components'; +import { Colors, Toggle, ToggleType, Type } from '@dash/components'; import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -832,6 +832,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { annotationLayerScrollTop={NumCast(this.Document._layout_scrollTop)} scaling={returnOne} docView={this.DocumentView} + screenTransform={this.DocumentView().screenToViewTransform} addDocument={this.sidebarAddDocument} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 7c601185e..88e59d30f 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Colors } from 'browndash-components'; +import { Colors } from '@dash/components'; import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { IDisposer } from 'mobx-utils'; diff --git a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx index a6a6a6b46..8ae29a88c 100644 --- a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx +++ b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx @@ -1,4 +1,4 @@ -import { IconButton } from 'browndash-components'; +import { IconButton } from '@dash/components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 14d7e9bf6..5a9442d2f 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components'; +import { ColorPicker, EditableText, IconButton, Size, Type } from '@dash/components'; import * as d3 from 'd3'; import { IReactionDisposer, action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index c2f5388a2..b55d509ff 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -1,4 +1,4 @@ -import { Button, EditableText, Size } from 'browndash-components'; +import { Button, EditableText, Size } from '@dash/components'; import * as d3 from 'd3'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 19ea8e4fa..86e6ad8e4 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -1,5 +1,5 @@ import { Checkbox } from '@mui/material'; -import { ColorPicker, EditableText, Size, Type } from 'browndash-components'; +import { ColorPicker, EditableText, Size, Type } from '@dash/components'; import * as d3 from 'd3'; import { IReactionDisposer, action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index fe596bc36..7ef4bca6b 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,4 +1,4 @@ -import { Button, Colors, Type } from 'browndash-components'; +import { Button, Colors, Type } from '@dash/components'; import { IReactionDisposer, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 8c138c2ee..60b2a7519 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { Button, ColorPicker, Dropdown, DropdownType, IconButton, IListItemProps, MultiToggle, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from '@dash/components'; import { action, computed, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 6b9e170c8..38393a5a0 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,14 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import axios from 'axios'; -import { Colors, Type } from 'browndash-components'; +import { Colors, Button, Type } from '@dash/components'; import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { extname } from 'path'; import * as React from 'react'; import ReactLoading from 'react-loading'; -import { ClientUtils, DashColor, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; -import { Doc, DocListCast, Opt, returnEmptyDoclist } from '../../../fields/Doc'; +import { ClientUtils, DashColor, returnEmptyString, returnFalse, returnOne, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../ClientUtils'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -34,17 +34,15 @@ import { AnchorMenu } from '../pdf/AnchorMenu'; import { PinDocView, PinProps } from '../PinFuncs'; import { StickerPalette } from '../smartdraw/StickerPalette'; import { StyleProp } from '../StyleProp'; -import { DocumentView, DocumentViewInternal } from './DocumentView'; +import { DocumentView } from './DocumentView'; import { FieldView, FieldViewProps } from './FieldView'; import { FocusViewOptions } from './FocusViewOptions'; import './ImageBox.scss'; import { OpenWhere } from './OpenWhere'; import { Upload } from '../../../server/SharedMediaTypes'; import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler'; -import { Button } from 'browndash-components'; import { SettingsManager } from '../../util/SettingsManager'; import { AiOutlineSend } from 'react-icons/ai'; -import { returnEmptyDocViewList } from '../StyleProvider'; import { FireflyImageData } from '../smartdraw/FireflyConstants'; export class ImageEditorData { @@ -274,7 +272,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const anchy = NumCast(cropping.y); const anchw = NumCast(cropping._width); const anchh = NumCast(cropping._height); - const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) / anchw; + const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) / anchh; cropping.title = 'crop: ' + this.Document.title; cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width); cropping.y = NumCast(this.Document.y); @@ -292,9 +290,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { croppingProto.data_nativeWidth = anchw; croppingProto.data_nativeHeight = anchh; croppingProto.freeform_scale = viewScale; - croppingProto.freeform_scale_min = viewScale; croppingProto.freeform_panX = anchx / viewScale; croppingProto.freeform_panY = anchy / viewScale; + croppingProto.freeform_scale_min = viewScale; croppingProto.freeform_panX_min = anchx / viewScale; croppingProto.freeform_panX_max = anchw / viewScale; croppingProto.freeform_panY_min = anchy / viewScale; @@ -541,6 +539,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { <div className="imageBox-aiView-history"> {this._prevImgs.map(img => ( <img + key={img.pathname} className="imageBox-aiView-img" src={img.href} onClick={() => { @@ -771,6 +770,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { annotationLayerScrollTop={0} scaling={returnOne} annotationLayerScaling={this._props.NativeDimScaling} + screenTransform={this.DocumentView().screenToViewTransform} docView={this.DocumentView} addDocument={this.addDocument} finishMarquee={this.finishMarquee} diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx index b8fd8ac6a..8784a709a 100644 --- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx @@ -1,6 +1,6 @@ import { IconLookup, faAdd, faCalendarDays, faRoute } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { IconButton } from 'browndash-components'; +import { IconButton } from '@dash/components'; import { IReactionDisposer, ObservableMap, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx index 103a35434..87469b50e 100644 --- a/src/client/views/nodes/MapBox/MapAnchorMenu.tsx +++ b/src/client/views/nodes/MapBox/MapAnchorMenu.tsx @@ -2,7 +2,7 @@ import { IconLookup, faAdd, faArrowDown, faArrowLeft, faArrowsRotate, faBicycle, faCalendarDays, faCar, faDiamondTurnRight, faEdit, faPersonWalking, faRoute } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Autocomplete, Checkbox, FormControlLabel, TextField } from '@mui/material'; -import { IconButton } from 'browndash-components'; +import { IconButton } from '@dash/components'; import { Position } from 'geojson'; import { IReactionDisposer, ObservableMap, action, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx index 4a436319b..c4bb7c47d 100644 --- a/src/client/views/nodes/MapBox/MapBox.tsx +++ b/src/client/views/nodes/MapBox/MapBox.tsx @@ -2,7 +2,7 @@ import { IconLookup, faCircleXmark, faGear, faPause, faPlay, faRotate } from '@f import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, FormControlLabel, TextField } from '@mui/material'; import * as turf from '@turf/turf'; -import { IconButton, Size, Type } from 'browndash-components'; +import { IconButton, Size, Type } from '@dash/components'; import * as d3 from 'd3'; import { Feature, FeatureCollection, GeoJsonProperties, Geometry, LineString, Position } from 'geojson'; import mapboxgl, { LngLatBoundsLike, MapLayerMouseEvent } from 'mapbox-gl'; diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx index d5d8f8afa..95f89a573 100644 --- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx +++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, EditableText, IconButton, Type } from 'browndash-components'; +import { Button, EditableText, IconButton, Type } from '@dash/components'; import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 7bca1230f..f6908d5fd 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -250,6 +250,17 @@ cursor: ew-resize; background: lightGray; } +.pdfBox-container { + position: absolute; + transform-origin: top left; + top: 0; +} +.pdfBox-sidebarContainer { + position: absolute; + height: 100%; + right: 0; + top: 0; +} .pdfBox-interactive { width: 100%; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 2e4c87d38..06b75e243 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as Pdfjs from 'pdfjs-dist'; import 'pdfjs-dist/web/pdf_viewer.css'; @@ -40,8 +40,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); } + static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>(); + static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>(); public static openSidebarWidth = 250; public static sidebarResizerWidth = 5; + private _searchString: string = ''; private _initialScrollTarget: Opt<Doc>; private _pdfViewer: PDFViewer | undefined; @@ -63,11 +66,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200; !this.Document._layout_fitWidth && (this.Document._height = NumCast(this.Document._width) * (nh / nw)); if (this.pdfUrl) { - if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) - runInAction(() => { - this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href); - }); - else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) + this._pdf = PDFBox.pdfcache.get(this.pdfUrl.url.href); + !this._pdf && PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then( action(pdf => { this._pdf = pdf; @@ -265,12 +265,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }; @action - loaded = (nw: number, nh: number, np: number) => { - this.dataDoc[this._props.fieldKey + '_numPages'] = np; - Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72)); - Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72); + loaded = (p: { width: number; height: number }, pages: number) => { + this.dataDoc[this._props.fieldKey + '_numPages'] = pages; + Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), p.width)); + Doc.SetNativeHeight(this.dataDoc, p.height); this.layoutDoc._height = NumCast(this.layoutDoc._width) / (Doc.NativeAspect(this.dataDoc) || 1); - !this.Document._layout_fitWidth && (this.Document._height = NumCast(this.Document._width) * (nh / nw)); + !this.Document._layout_fitWidth && (this.Document._height = NumCast(this.Document._width) * (p.height / p.width)); }; override search = action((searchString: string, bwd?: boolean, clear: boolean = false) => { @@ -584,7 +584,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @computed get renderPdfView() { TraceMobx(); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; - const scale = previewScale * (this._props.NativeDimScaling?.() || 1); + // PDFjs scales page renderings to be the render container size times the ratio of CSS/print pixels. + // So we have to scale the render container down by this ratio, so that the renderings will match the size of the container + const viewScale = (previewScale * (this._props.NativeDimScaling?.() || 1)) / Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS; return !this._pdf ? null : ( <div className="pdfBox" @@ -594,13 +596,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }}> <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} /> <div + className="pdfBox-container" style={{ - width: `calc(${100 / scale}% - ${(this.sidebarWidth() / scale) * (this._previewWidth ? scale : 1)}px)`, - height: `${100 / scale}%`, - transform: `scale(${scale})`, - position: 'absolute', - transformOrigin: 'top left', - top: 0, + width: `calc(${100 / viewScale}% - ${(this.sidebarWidth() / viewScale) * (this._previewWidth ? viewScale : 1)}px)`, + height: `${100 / viewScale}%`, + transform: `scale(${viewScale})`, }}> <PDFViewer {...this._props} @@ -613,7 +613,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { focus={this.focus} url={this.pdfUrl!.url.pathname} anchorMenuClick={this.anchorMenuClick} - loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined} + loaded={Doc.NativeAspect(this.dataDoc) ? emptyFunction : this.loaded} setPdfViewer={this.setPdfViewer} addDocument={this.addDocument} moveDocument={this.moveDocument} @@ -622,14 +622,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { crop={this.crop} /> </div> - <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}>{this.sidebarCollection}</div> + <div className="pdfBox-sidebarContainer" style={{ width: `calc(100 * ${this.sidebarWidth() / this._props.PanelWidth()}%` }}> + {this.sidebarCollection} + </div> {this.settingsPanel()} </div> ); } - static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>(); - static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>(); render() { TraceMobx(); const pdfView = !this._pdf ? null : this.renderPdfView; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 3bf7de2fe..9adee53e8 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -1023,6 +1023,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { scaling={returnOne} annotationLayerScaling={this._props.NativeDimScaling} docView={this.DocumentView} + screenTransform={this.DocumentView().screenToViewTransform} containerOffset={this.marqueeOffset} addDocument={this.addDocWithTimecode} finishMarquee={this.finishMarquee} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 9ba4f8ead..6026d9ca7 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -202,8 +202,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { () => this.layoutDoc._layout_autoHeight, layoutAutoHeight => { if (layoutAutoHeight) { - this.layoutDoc._nativeHeight = NumCast(this.Document[this._props.fieldKey + '_nativeHeight']); - this._props.setHeight?.(NumCast(this.Document[this._props.fieldKey + '_nativeHeight']) * (this._props.NativeDimScaling?.() || 1)); + const nh = NumCast(this.Document[this._props.fieldKey + '_nativeHeight'], NumCast(this.Document.nativeHeight)); + this.layoutDoc._nativeHeight = nh; + this._props.setHeight?.(nh * (this._props.NativeDimScaling?.() || 1)); } } ); @@ -1195,6 +1196,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { scaling={this._props.NativeDimScaling} addDocument={this.addDocumentWrapper} docView={this.DocumentView} + screenTransform={this.DocumentView().screenToViewTransform} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotationsCreator} selectionText={this.selectionText} diff --git a/src/client/views/nodes/imageEditor/GenerativeFillButtons.tsx b/src/client/views/nodes/imageEditor/GenerativeFillButtons.tsx index 32ed6b307..fe9c39aad 100644 --- a/src/client/views/nodes/imageEditor/GenerativeFillButtons.tsx +++ b/src/client/views/nodes/imageEditor/GenerativeFillButtons.tsx @@ -1,7 +1,7 @@ import './GenerativeFillButtons.scss'; import * as React from 'react'; import ReactLoading from 'react-loading'; -import { Button, IconButton, Type } from 'browndash-components'; +import { Button, IconButton, Type } from '@dash/components'; import { AiOutlineInfo } from 'react-icons/ai'; import { activeColor } from './imageEditorUtils/imageEditorConstants'; diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx index 2a8bc034d..809ced2f6 100644 --- a/src/client/views/nodes/imageEditor/ImageEditor.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx @@ -1,10 +1,7 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ -/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ -/* eslint-disable jsx-a11y/img-redundant-alt */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable no-use-before-define */ /* eslint-disable react/function-component-definition */ import { Checkbox, FormControlLabel, Slider, TextField } from '@mui/material'; -import { Button, IconButton, Type } from 'browndash-components'; +import { Button, IconButton, Type } from '@dash/components'; import * as React from 'react'; import { useEffect, useRef, useState } from 'react'; import { CgClose } from 'react-icons/cg'; @@ -495,7 +492,7 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc }; // creates a new image document and returns its reference - const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean, parent?: Doc): Promise<Doc | undefined> => { + const createNewImgDoc = async (img: HTMLImageElement, firstDoc: boolean /*, parent?: Doc */): Promise<Doc | undefined> => { if (!imageRootDoc) return undefined; const { src } = img; const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [src] }); diff --git a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx index cb963616b..de2116253 100644 --- a/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx +++ b/src/client/views/nodes/imageEditor/ImageEditorButtons.tsx @@ -1,7 +1,7 @@ import './GenerativeFillButtons.scss'; import * as React from 'react'; import ReactLoading from 'react-loading'; -import { Button, IconButton, Type } from 'browndash-components'; +import { Button, IconButton, Type } from '@dash/components'; import { AiOutlineInfo } from 'react-icons/ai'; import { bgColor } from './imageEditorUtils/imageEditorConstants'; import { ImageEditTool, ImageToolType } from './imageEditorUtils/imageEditorInterfaces'; diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 06869717a..f23b32a48 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import Slider from '@mui/material/Slider'; -import { Button, Dropdown, DropdownType, IconButton, Toggle, ToggleType, Type } from 'browndash-components'; +import { Button, Dropdown, DropdownType, IconButton, Toggle, ToggleType, Type } from '@dash/components'; import { IReactionDisposer, ObservableSet, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 2e704aa8d..11f2f7988 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from '@dash/components'; import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index a7e78bcea..22ca9dcd7 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Type } from 'browndash-components'; +import { Button, IconButton, Type } from '@dash/components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/pdf/PDFViewer.scss b/src/client/views/pdf/PDFViewer.scss index a225c4b59..030251762 100644 --- a/src/client/views/pdf/PDFViewer.scss +++ b/src/client/views/pdf/PDFViewer.scss @@ -7,6 +7,10 @@ left: 0; } +:root { + --devicePixelRatio: 1; // the actual value of this will be set in PDFViewer.tsx; +} + .pdfViewerDash, .pdfViewerDash-interactive { position: absolute; @@ -19,9 +23,16 @@ overflow-x: hidden; transform-origin: top left; + .annotationLayer { + transform: scale(var(--devicePixelRatio)); + } .textLayer { opacity: unset; mix-blend-mode: multiply; // bcz: makes text fuzzy! + transform: scale(var(--devicePixelRatio)); + } + [data-main-rotation='90'] { + transform: scale(var(--devicePixelRatio)) rotate(90deg) translateY(-100%); } .textLayer ::selection { background: #accef76a; @@ -39,6 +50,7 @@ .page { position: relative; border: unset; + height: 100% !important; } .pdfViewerDash-text-selected { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 920c9ea8b..8728ce99c 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -11,7 +11,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, numberRange } from '../../../Utils'; import { DocUtils } from '../../documents/DocUtils'; import { SnappingManager } from '../../util/SnappingManager'; import { MarqueeOptionsMenu } from '../collections/collectionFreeForm'; @@ -30,6 +30,7 @@ import { GPTPopup } from './GPTPopup/GPTPopup'; import './PDFViewer.scss'; import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; import ReactLoading from 'react-loading'; +import { Transform } from '../../util/Transform'; interface IViewerProps extends FieldViewProps { pdfBox: PDFBox; @@ -40,7 +41,7 @@ interface IViewerProps extends FieldViewProps { pdf: Pdfjs.PDFDocumentProxy; url: string; sidebarAddDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean; - loaded?: (nw: number, nh: number, np: number) => void; + loaded: (p: { width: number; height: number }, pages: number) => void; // eslint-disable-next-line no-use-before-define setPdfViewer: (view: PDFViewer) => void; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); @@ -146,32 +147,30 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { } }; - @observable _scrollHeight = 0; + @computed get _scrollHeight() { + return this._pageSizes.reduce((size, page) => size + page.height, 0); + } - @action - initialLoad = async () => { + initialLoad = () => { + const page0or180 = (page: { rotate: number }) => page.rotate === 0 || page.rotate === 180; if (this._pageSizes.length === 0) { - this._pageSizes = Array<{ width: number; height: number }>(this._props.pdf.numPages); - await Promise.all( - this._pageSizes.map((val, i) => - this._props.pdf.getPage(i + 1).then( - action((page: Pdfjs.PDFPageProxy) => { - const page0or180 = page.rotate === 0 || page.rotate === 180; - this._pageSizes.splice(i, 1, { - width: page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], - height: page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], - }); - if (i === this._props.pdf.numPages - 1) { - this._props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this._props.pdf.numPages); - } - }) - ) + const devicePixelRatio = window.devicePixelRatio; + document.documentElement?.style.setProperty('--devicePixelRatio', window.devicePixelRatio.toString()); // set so that css can use this to adjust various PDFJs divs + Promise.all( + numberRange(this._props.pdf.numPages).map(i => + this._props.pdf.getPage(i + 1).then(page => ({ + width: (page.view[page0or180(page) ? 2 : 3] - page.view[page0or180(page) ? 0 : 1]) * devicePixelRatio, + height: (page.view[page0or180(page) ? 3 : 2] - page.view[page0or180(page) ? 1 : 0]) * devicePixelRatio, + })) ) + ).then( + action(pages => { + this._pageSizes = pages; + this._props.loaded(pages.lastElement(), this._props.pdf.numPages); + this.createPdfViewer(); + }) ); } - runInAction(() => { - this._scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72; - }); }; _scrollStopper: undefined | (() => void); @@ -197,14 +196,12 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { crop = (region: Doc | undefined, addCrop?: boolean) => this._props.crop(region, addCrop); @action - setupPdfJsViewer = async () => { + setupPdfJsViewer = () => { if (this._viewerIsSetup) return; this._viewerIsSetup = true; this._showWaiting = true; this._props.setPdfViewer(this); - await this.initialLoad(); - - this.createPdfViewer(); + this.initialLoad(); }; pagesinit = () => { @@ -533,7 +530,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { } getScrollHeight = () => this._scrollHeight; - scrollXf = () => this._props.ScreenToLocalTransform().translate(0, this._mainCont.current ? NumCast(this._props.layoutDoc._layout_scrollTop) : 0); + scrollXf = () => this._props.ScreenToLocalTransform().translate(0, this._mainCont.current ? NumCast(this._props.layoutDoc._layout_scrollTop) / 1.333 : 0); overlayTransform = () => this.scrollXf().scale(1 / NumCast(this._props.layoutDoc._freeform_scale, 1)); panelWidth = () => this._props.PanelWidth() / (this._props.NativeDimScaling?.() || 1); panelHeight = () => this._props.PanelHeight() / (this._props.NativeDimScaling?.() || 1); @@ -554,7 +551,8 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { className="pdfViewerDash-overlay" style={{ mixBlendMode, - display: display, + display, + transform: `scale(${Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS})`, pointerEvents: Doc.ActiveTool !== InkTool.None ? 'all' : undefined, }}> <CollectionFreeFormView @@ -600,6 +598,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { } savedAnnotations = () => this._savedAnnotations; addDocumentWrapper = (doc: Doc | Doc[]) => this._props.addDocument!(doc); + screenToMarqueeXf = () => this.props.pdfBox.DocumentView?.()?.screenToContentsTransform().scale(Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS) ?? Transform.Identity(); render() { TraceMobx(); return ( @@ -619,17 +618,18 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { {this.annotationLayer} {this.overlayLayer} {this._showWaiting ? <img alt="" className="pdfViewerDash-waiting" src="/assets/loading.gif" /> : null} - {!this._mainCont.current || !this._annotationLayer.current ? null : ( + {!this._mainCont.current || !this._annotationLayer.current || !this.props.pdfBox.DocumentView ? null : ( <MarqueeAnnotator ref={this._marqueeref} Document={this._props.Document} getPageFromScroll={this.getPageFromScroll} anchorMenuClick={this._props.anchorMenuClick} scrollTop={0} - isNativeScaled + annotationLayerScaling={() => Pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS} annotationLayerScrollTop={NumCast(this._props.Document._layout_scrollTop)} addDocument={this.addDocumentWrapper} - docView={this._props.pdfBox.DocumentView!} + docView={this.props.pdfBox.DocumentView} + screenTransform={this.screenToMarqueeXf} finishMarquee={this.finishMarquee} savedAnnotations={this.savedAnnotations} selectionText={this.selectionText} diff --git a/src/client/views/selectedDoc/SelectedDocView.tsx b/src/client/views/selectedDoc/SelectedDocView.tsx index 78a1a92f7..49cdc6bf8 100644 --- a/src/client/views/selectedDoc/SelectedDocView.tsx +++ b/src/client/views/selectedDoc/SelectedDocView.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { ListBox } from 'browndash-components'; +import { ListBox } from '@dash/components'; import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index af5fd727f..9248cbee3 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -1,6 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, FormControlLabel, Radio, RadioGroup, Slider, Switch } from '@mui/material'; -import { Button, IconButton } from 'browndash-components'; +import { Button, IconButton } from '@dash/components'; import { action, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; @@ -26,6 +26,7 @@ import './SmartDrawHandler.scss'; import { Networking } from '../../Network'; import { OpenWhere } from '../nodes/OpenWhere'; import { FireflyDimensionsMap, FireflyImageDimensions, FireflyImageData } from './FireflyConstants'; +import { DocumentType } from '../../documents/DocumentTypes'; export interface DrawingOptions { text: string; @@ -278,13 +279,13 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { nativeWidth: dims.width, nativeHeight: dims.height, ai: 'firefly', - ai_firefly_seed: seed, + ai_firefly_seed: aiseed, ai_firefly_prompt: input, }); DocumentViewInternal.addDocTabFunc(imgDoc, OpenWhere.addRight); this._selectedDocs.push(imgDoc); } - return { prompt: input, seed: seed, pathname: img.accessPaths.agnostic.client }; + return { prompt: input, seed, pathname: img.accessPaths.agnostic.client }; }); }; @@ -293,44 +294,43 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { * @param doc the drawing Docs to regenerate */ @action - regenerate = async (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => { + regenerate = (drawingDocs: Doc[], lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string, changeInPlace?: boolean) => { if (lastInput) this._lastInput = lastInput; if (lastResponse) this._lastResponse = lastResponse; if (regenInput) this._regenInput = regenInput; - return await Promise.all( + return Promise.all( drawingDocs.map(async doc => { - const docData = doc[DocData]; - if (docData.type == 'image') { - const seed: number = docData?.ai_firefly_seed as number; - if (this._regenInput !== '') { - // if (this._selectedDoc) { - const newPrompt = `${docData.ai_firefly_prompt} ~~~ ${this._regenInput}`; - return this.createImageWithFirefly(newPrompt, seed, changeInPlace); - // } - } else { - return this.createImageWithFirefly(this._lastInput.text || StrCast(docData.ai_firefly_prompt), undefined, changeInPlace); - } - } - if (docData.type == 'collection') { - try { - let res; - if (this._regenInput !== '') { - const prompt: string = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`; - res = await gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true); - this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`; - } else { - res = await gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true); + switch (doc.type) { + case DocumentType.IMG: + if (this._regenInput) { + // if (this._selectedDoc) { + const newPrompt = `${doc.ai_firefly_prompt} ~~~ ${this._regenInput}`; + return this.createImageWithFirefly(newPrompt, NumCast(doc?.ai_firefly_seed), changeInPlace); + // } } - if (!res) { - console.error('GPT call failed'); - return; + return this.createImageWithFirefly(this._lastInput.text || StrCast(doc.ai_firefly_prompt), undefined, changeInPlace); + case DocumentType.COL: { + try { + let res; + if (this._regenInput) { + const prompt = `This is your previously generated svg code: ${this._lastResponse} for the user input "${this._lastInput.text}". Please regenerate it with the provided specifications.`; + res = await gptAPICall(`"${this._regenInput}"`, GPTCallType.DRAW, prompt, true); + this._lastInput.text = `${this._lastInput.text} ~~~ ${this._regenInput}`; + } else { + res = await gptAPICall(`"${this._lastInput.text}", "${this._lastInput.complexity}", "${this._lastInput.size}"`, GPTCallType.DRAW, undefined, true); + } + if (res) { + const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); + this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc); + const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes); + drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); + } else { + console.error('GPT call failed'); + } + } catch (err) { + console.error('Error regenerating drawing', err); } - const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); - this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(true, doc); - const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData.data, strokeData.lastInput, strokeData.lastRes); - drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res); - } catch (err) { - console.error('Error regenerating drawing', err); + break; } } }) @@ -340,7 +340,6 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { /** * Parses the svg code that GPT returns into Bezier curves, with coordinates and colors. */ - @action parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => { const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g); if (svg) { @@ -401,7 +400,108 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { }); }, 'color strokes'); - renderDisplay() { + renderGenerateOutputOptions = () => ( + <div className="smartdraw-output-options"> + <div className="drawing-checkbox"> + Generate Ink + <Checkbox + sx={{ + color: 'white', + '&.Mui-checked': { + color: SettingsManager.userVariantColor, + }, + }} + checked={this._generateDrawing} + onChange={() => this._canInteract && (this._generateDrawing = !this._generateDrawing)} + /> + </div> + <div className="image-checkbox"> + Generate Image + <Checkbox + sx={{ + color: 'white', + '&.Mui-checked': { + color: SettingsManager.userVariantColor, + }, + }} + checked={this._generateImage} + onChange={() => this._canInteract && (this._generateImage = !this._generateImage)} + /> + </div> + </div> + ); + + renderGenerateDrawing = () => ( + <div className="smartdraw-options-container"> + Drawing Options + <div className="smartdraw-options"> + <div className="smartdraw-auto-color"> + Auto color + <Switch + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { color: SettingsManager.userColor }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { backgroundColor: SettingsManager.userVariantColor }, + }} + defaultChecked={true} + value={this._autoColor} + size="small" + onChange={action(() => this._canInteract && (this._autoColor = !this._autoColor))} + /> + </div> + <div className="smartdraw-complexity"> + Complexity + <Slider + className="smartdraw-slider" + sx={{ + '& .MuiSlider-track': { color: SettingsManager.userVariantColor }, + '& .MuiSlider-rail': { color: SettingsManager.userColor }, + '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } }, + }} + min={1} + max={10} + step={1} + size="small" + value={this._complexity} + onChange={action((e, val) => this._canInteract && (this._complexity = val as number))} + valueLabelDisplay="auto" + /> + </div> + <div className="smartdraw-size"> + Size (in pixels) + <Slider + className="smartdraw-slider" + sx={{ + '& .MuiSlider-track': { color: SettingsManager.userVariantColor }, + '& .MuiSlider-rail': { color: SettingsManager.userColor }, + '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20` } }, + }} + min={50} + max={700} + step={10} + size="small" + value={this._size} + onChange={action((e, val) => this._canInteract && (this._size = val as number))} + valueLabelDisplay="auto" + /> + </div> + </div> + </div> + ); + + renderGenerateImage = () => ( + <div className="smartdraw-options-container"> + Image Options + <div className="smartdraw-dimensions"> + <RadioGroup row defaultValue="square" sx={{ alignItems: 'center' }}> + {Object.values(FireflyImageDimensions).map(dim => ( + <FormControlLabel sx={{ width: '40%' }} key={dim} value={dim} control={<Radio />} onChange={() => this._canInteract && (this._imgDims = dim)} label={dim} /> + ))} + </RadioGroup> + </div> + </div> + ); + + renderDisplay = () => { return ( <div className="smart-draw-handler" @@ -444,154 +544,64 @@ export class SmartDrawHandler extends ObservableReactComponent<object> { </div> {this._showOptions && ( <div> - <div className="smartdraw-output-options"> - <div className="drawing-checkbox"> - Generate Ink - <Checkbox - sx={{ - color: 'white', - '&.Mui-checked': { - color: SettingsManager.userVariantColor, - }, - }} - checked={this._generateDrawing} - onChange={() => this._canInteract && (this._generateDrawing = !this._generateDrawing)} - /> - </div> - <div className="image-checkbox"> - Generate Image - <Checkbox - sx={{ - color: 'white', - '&.Mui-checked': { - color: SettingsManager.userVariantColor, - }, - }} - checked={this._generateImage} - onChange={() => this._canInteract && (this._generateImage = !this._generateImage)} - /> - </div> - </div> - {this._generateDrawing && ( - <div className="smartdraw-options-container"> - Drawing Options - <div className="smartdraw-options"> - <div className="smartdraw-auto-color"> - Auto color - <Switch - sx={{ - '& .MuiSwitch-switchBase.Mui-checked': { color: SettingsManager.userColor }, - '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { backgroundColor: SettingsManager.userVariantColor }, - }} - defaultChecked={true} - value={this._autoColor} - size="small" - onChange={action(() => this._canInteract && (this._autoColor = !this._autoColor))} - /> - </div> - <div className="smartdraw-complexity"> - Complexity - <Slider - className="smartdraw-slider" - sx={{ - '& .MuiSlider-track': { color: SettingsManager.userVariantColor }, - '& .MuiSlider-rail': { color: SettingsManager.userColor }, - '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } }, - }} - min={1} - max={10} - step={1} - size="small" - value={this._complexity} - onChange={action((e, val) => this._canInteract && (this._complexity = val as number))} - valueLabelDisplay="auto" - /> - </div> - <div className="smartdraw-size"> - Size (in pixels) - <Slider - className="smartdraw-slider" - sx={{ - '& .MuiSlider-track': { color: SettingsManager.userVariantColor }, - '& .MuiSlider-rail': { color: SettingsManager.userColor }, - '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20` } }, - }} - min={50} - max={700} - step={10} - size="small" - value={this._size} - onChange={action((e, val) => this._canInteract && (this._size = val as number))} - valueLabelDisplay="auto" - /> - </div> - </div> - </div> - )} - {this._generateImage && ( - <div className="smartdraw-options-container"> - Image Options - <div className="smartdraw-dimensions"> - <RadioGroup row defaultValue="square" sx={{ alignItems: 'center' }}> - <FormControlLabel sx={{ width: '40%' }} value="square" control={<Radio />} onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Square)} label="Square" /> - <FormControlLabel sx={{ width: '40%' }} value="landscape" control={<Radio />} onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Landscape)} label="Landscape" /> - <FormControlLabel sx={{ width: '40%' }} value="portrait" control={<Radio />} onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Portrait)} label="Portrait" /> - <FormControlLabel sx={{ width: '40%' }} value="widescreen" control={<Radio />} onChange={() => this._canInteract && (this._imgDims = FireflyImageDimensions.Widescreen)} label="Widescreen" /> - </RadioGroup> - </div> - </div> - )} + {this.renderGenerateOutputOptions()} + {this._generateDrawing ? this.renderGenerateDrawing() : null} + {this._generateImage ? this.renderGenerateImage() : null} </div> )} </div> ); - } + }; - renderRegenerate() { - return ( - <div - className="smart-draw-handler" - style={{ - left: this._pageX, - ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }), - background: SettingsManager.userBackgroundColor, - color: SettingsManager.userColor, - }}> - <div className="regenerate-box"> - <IconButton - tooltip="Regenerate" - icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />} - color={SettingsManager.userColor} - onClick={this.handleSendClick} - /> - <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} /> - {this._showEditBox && ( - <div className="edit-box"> - <input - aria-label="Edit instructions input" - className="smartdraw-input" - type="text" - value={this._regenInput} - onChange={action(e => this._canInteract && (this._regenInput = e.target.value))} - onKeyDown={this.handleKeyPress} - placeholder="Edit instructions" - /> - <Button - style={{ alignSelf: 'flex-end' }} - text="Send" - icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} - iconPlacement="right" - color={SettingsManager.userColor} - onClick={this.handleSendClick} - /> - </div> - )} - </div> + renderRegenerateEditBox = () => ( + <div className="edit-box"> + <input + aria-label="Edit instructions input" + className="smartdraw-input" + type="text" + value={this._regenInput} + onChange={action(e => this._canInteract && (this._regenInput = e.target.value))} + onKeyDown={this.handleKeyPress} + placeholder="Edit instructions" + /> + <Button + style={{ alignSelf: 'flex-end' }} + text="Send" + icon={this._isLoading && this._regenInput !== '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />} + iconPlacement="right" + color={SettingsManager.userColor} + onClick={this.handleSendClick} + /> + </div> + ); + + renderRegenerate = () => ( + <div + className="smart-draw-handler" + style={{ + left: this._pageX, + ...(this._yRelativeToTop ? { top: Math.max(0, this._pageY) } : { bottom: this._pageY }), + background: SettingsManager.userBackgroundColor, + color: SettingsManager.userColor, + }}> + <div className="regenerate-box"> + <IconButton + tooltip="Regenerate" + icon={this._isLoading && this._regenInput === '' ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <FontAwesomeIcon icon={'rotate'} />} + color={SettingsManager.userColor} + onClick={this.handleSendClick} + /> + <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={action(() => (this._showEditBox = !this._showEditBox))} /> + {this._showEditBox ? this.renderRegenerateEditBox() : null} </div> - ); - } + </div> + ); render() { - return this._display ? this.renderDisplay() : this.ShowRegenerate ? this.renderRegenerate() : null; + return this._display + ? this.renderDisplay() // + : this.ShowRegenerate + ? this.renderRegenerate() + : null; } } diff --git a/src/client/views/smartdraw/StickerPalette.tsx b/src/client/views/smartdraw/StickerPalette.tsx index d23763eb9..d5307974f 100644 --- a/src/client/views/smartdraw/StickerPalette.tsx +++ b/src/client/views/smartdraw/StickerPalette.tsx @@ -1,13 +1,13 @@ +import { Button } from '@dash/components'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Slider, Switch } from '@mui/material'; -import { Button } from 'browndash-components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { AiOutlineSend } from 'react-icons/ai'; import ReactLoading from 'react-loading'; import { returnEmptyFilter, returnFalse, returnTrue } from '../../../ClientUtils'; -import { emptyFunction } from '../../../Utils'; +import { emptyFunction, numberRange } from '../../../Utils'; import { Doc, DocListCast, returnEmptyDoclist } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { ImageCast, NumCast } from '../../../fields/Types'; @@ -22,13 +22,18 @@ import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider, returnEmptyDocViewList } from '../StyleProvider'; import { DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldView } from '../nodes/FieldView'; -import './StickerPalette.scss'; import { DrawingOptions, SmartDrawHandler } from './SmartDrawHandler'; +import './StickerPalette.scss'; interface StickerPaletteProps { Document: Doc; } +enum StickerPaletteMode { + create, + view, +} + /** * The StickerPalette can be toggled in the lightbox view of a document. The goal of the palette * is to offer an easy way for users to create stickers and drag and drop them onto a document. @@ -41,7 +46,34 @@ interface StickerPaletteProps { */ @observer export class StickerPalette extends ObservableReactComponent<StickerPaletteProps> { - @observable private _paletteMode: 'create' | 'view' = 'view'; + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(StickerPalette, fieldKey); + } + /** + * Adds a doc to the sticker palette. Gets a snapshot of the document to use as a preview in the palette. When this + * preview is dragged onto a parent document, a copy of that document is added as a sticker. + */ + public static addToPalette = async (doc: Doc) => { + if (!doc.savedAsSticker) { + const docView = DocumentView.getDocumentView(doc); + await docView?.ComponentView?.updateIcon?.(true); + const { clone } = await Doc.MakeClone(doc); + clone.title = doc.title; + const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href; + Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image)); + doc.savedAsSticker = true; + } + }; + + public static getIcon(group: Doc) { + const docView = DocumentView.getDocumentView(group); + docView?.ComponentView?.updateIcon?.(true); + return !docView ? undefined : new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); + } + + private _gptRes: string[] = []; + + @observable private _paletteMode = StickerPaletteMode.view; @observable private _userInput: string = ''; @observable private _isLoading: boolean = false; @observable private _canInteract: boolean = true; @@ -49,7 +81,6 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps @observable private _docView: DocumentView | null = null; @observable private _docCarouselView: DocumentView | null = null; @observable private _opts: DrawingOptions = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; - private _gptRes: string[] = []; constructor(props: StickerPaletteProps) { super(props); @@ -60,51 +91,40 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps this.resetPalette(true); } - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(StickerPalette, fieldKey); - } - - Contains = (view: DocumentView) => { - return (this._docView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docView)) || (this._docCarouselView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docCarouselView)); - }; + Contains = (view: DocumentView) => + (this._docView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docView)) || // + (this._docCarouselView && (view.containerViewPath?.() ?? []).concat(view).includes(this._docCarouselView)); return170 = () => 170; - @action - handleKeyPress = async (event: React.KeyboardEvent) => { + handleKeyPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - await this.generateDrawings(); + this.generateDrawings(); } }; - @action - setPaletteMode = (mode: 'create' | 'view') => { + setPaletteMode = action((mode: StickerPaletteMode) => { this._paletteMode = mode; - }; + }); - @action - setUserInput = (input: string) => { + setUserInput = action((input: string) => { if (!this._isLoading) this._userInput = input; - }; + }); - @action - setDetail = (detail: number) => { + setDetail = action((detail: number) => { if (this._canInteract) this._opts.complexity = detail; - }; + }); - @action - setColor = (autoColor: boolean) => { + setColor = action((autoColor: boolean) => { if (this._canInteract) this._opts.autoColor = autoColor; - }; + }); - @action - setSize = (size: number) => { + setSize = action((size: number) => { if (this._canInteract) this._opts.size = size; - }; + }); - @action - resetPalette = (changePaletteMode: boolean) => { - if (changePaletteMode) this.setPaletteMode('view'); + resetPalette = action((changePaletteMode: boolean) => { + if (changePaletteMode) this.setPaletteMode(StickerPaletteMode.view); this.setUserInput(''); this.setDetail(5); this.setColor(true); @@ -114,55 +134,31 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; this._gptRes = []; this._props.Document[DocData].data = undefined; - }; - - /** - * Adds a doc to the sticker palette. Gets a snapshot of the document to use as a preview in the palette. When this - * preview is dragged onto a parent document, a copy of that document is added as a sticker. - */ - public static addToPalette = async (doc: Doc) => { - if (!doc.savedAsSticker) { - const docView = DocumentView.getDocumentView(doc); - await docView?.ComponentView?.updateIcon?.(true); - const { clone } = await Doc.MakeClone(doc); - clone.title = doc.title; - const image = ImageCast(doc.icon, ImageCast(clone[Doc.LayoutFieldKey(clone)]))?.url?.href; - Doc.AddDocToList(Doc.MyStickers, 'data', makeUserTemplateButtonOrImage(clone, image)); - doc.savedAsSticker = true; - } - }; - - public static getIcon(group: Doc) { - const docView = DocumentView.getDocumentView(group); - if (docView) { - docView.ComponentView?.updateIcon?.(true); - return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); - } - return undefined; - } + }); /** * Calls the draw with AI functions in SmartDrawHandler to allow users to generate drawings straight from * the sticker palette. */ @undoBatch - generateDrawings = action(async () => { + generateDrawings = action(() => { this._isLoading = true; const prevDrawings = DocListCast(this._props.Document[DocData].data); this._props.Document[DocData].data = undefined; SmartDrawHandler.Instance.AddDrawing = this.addDrawing; this._canInteract = false; - await Promise.all( - Array.from({ length: 3 }).map((_, i) => { + Promise.all( + numberRange(3).map(i => { return this._showRegenerate ? SmartDrawHandler.Instance.regenerate(prevDrawings, this._opts, this._gptRes[i], this._userInput) : SmartDrawHandler.Instance.drawWithGPT({ X: 0, Y: 0 }, this._userInput, this._opts.complexity, this._opts.size, this._opts.autoColor); }) - ); - this._opts.text !== '' ? (this._opts.text = `${this._opts.text} ~~~ ${this._userInput}`) : (this._opts.text = this._userInput); - this._userInput = ''; - this._isLoading = false; - this._showRegenerate = true; + ).then(() => { + this._opts.text !== '' ? (this._opts.text = `${this._opts.text} ~~~ ${this._userInput}`) : (this._opts.text = this._userInput); + this._userInput = ''; + this._isLoading = false; + this._showRegenerate = true; + }); }); @action @@ -177,7 +173,7 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps * AddToPalette() is generically used to add any document to the palette, while this defines the behavior for when a user * presses the "save drawing" button. */ - saveDrawing = async () => { + saveDrawing = () => { const cIndex = NumCast(this._props.Document.carousel_index); const focusedDrawing = DocListCast(this._props.Document.data)[cIndex]; const docData = focusedDrawing[DocData]; @@ -191,168 +187,160 @@ export class StickerPalette extends ObservableReactComponent<StickerPaletteProps focusedDrawing.width = this._opts.size; docData.x = this._opts.x; docData.y = this._opts.y; - await StickerPalette.addToPalette(focusedDrawing); - this.resetPalette(true); + StickerPalette.addToPalette(focusedDrawing).then(() => this.resetPalette(true)); + }; + + renderCreateInput = () => ( + <div className="palette-create"> + <input + className="palette-create-input" + aria-label="label-input" + id="new-label" + type="text" + value={this._userInput} + onChange={e => this.setUserInput(e.target.value)} + placeholder={this._showRegenerate ? '(Optional) Enter edits' : 'Enter item to draw'} + onKeyDown={this.handleKeyPress} + /> + <Button + style={{ alignSelf: 'flex-end' }} + tooltip={this._showRegenerate ? 'Regenerate' : 'Send'} + icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : this._showRegenerate ? <FontAwesomeIcon icon={'rotate'} /> : <AiOutlineSend />} + iconPlacement="right" + color={SettingsManager.userColor} + onClick={this.generateDrawings} + /> + </div> + ); + renderCreateOptions = () => ( + <div className="palette-create-options"> + <div className="palette-color"> + Color + <Switch + sx={{ + '& .MuiSwitch-switchBase.Mui-checked': { + color: SettingsManager.userColor, + }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { + backgroundColor: SettingsManager.userVariantColor, + }, + }} + defaultChecked={true} + value={this._opts.autoColor} + size="small" + onChange={() => this.setColor(!this._opts.autoColor)} + /> + </div> + <div className="palette-detail"> + Detail + <Slider + sx={{ + '& .MuiSlider-thumb': { + color: SettingsManager.userColor, + '&.Mui-focusVisible, &:hover, &.Mui-active': { + boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10`, + }, + }, + '& .MuiSlider-track': { + color: SettingsManager.userVariantColor, + }, + '& .MuiSlider-rail': { + color: SettingsManager.userColor, + }, + }} + style={{ width: '80%' }} + min={1} + max={10} + step={1} + size="small" + value={this._opts.complexity} + onChange={(e, val) => typeof val === 'number' && this.setDetail(val)} + valueLabelDisplay="auto" + /> + </div> + <div className="palette-size"> + Size + <Slider + sx={{ + '& .MuiSlider-thumb': { + color: SettingsManager.userColor, + '&.Mui-focusVisible, &:hover, &.Mui-active': { + boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20`, + }, + }, + '& .MuiSlider-track': { + color: SettingsManager.userVariantColor, + }, + '& .MuiSlider-rail': { + color: SettingsManager.userColor, + }, + }} + style={{ width: '80%' }} + min={50} + max={500} + step={10} + size="small" + value={this._opts.size} + onChange={(e, val) => typeof val === 'number' && this.setSize(val)} + valueLabelDisplay="auto" + /> + </div> + </div> + ); + renderDoc = (doc: Doc, refFunc: (r: DocumentView) => void) => { + return ( + <DocumentView + ref={refFunc} + Document={doc} + addDocument={undefined} + addDocTab={DocumentViewInternal.addDocTabFunc} + pinToPres={DocumentView.PinDoc} + containerViewPath={returnEmptyDocViewList} + styleProvider={DefaultStyleProvider} + removeDocument={returnFalse} + ScreenToLocalTransform={Transform.Identity} + PanelWidth={this.return170} + PanelHeight={this.return170} + renderDepth={1} + isContentActive={returnTrue} + focus={emptyFunction} + whenChildContentsActiveChanged={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + /> + ); }; + renderPaletteCreate = () => ( + <> + {this.renderCreateInput()} + {this.renderCreateOptions()} + {this.renderDoc(this._props.Document, (r: DocumentView) => { + this._docCarouselView = r; + })} + <div className="palette-buttons"> + <Button text="Back" tooltip="Back to All Stickers" icon={<FontAwesomeIcon icon="reply" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(true)} /> + <div className="palette-save-reset"> + <Button tooltip="Save" icon={<FontAwesomeIcon icon="file-arrow-down" />} color={SettingsManager.userColor} onClick={this.saveDrawing} /> + <Button tooltip="Reset" icon={<FontAwesomeIcon icon="rotate-left" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(false)} /> + </div> + </div> + </> + ); + renderPaletteView = () => ( + <> + {this.renderDoc(Doc.MyStickers, (r: DocumentView) => { + this._docView = r; + })} + <Button text="Add" icon={<FontAwesomeIcon icon="square-plus" />} color={SettingsManager.userColor} onClick={() => this.setPaletteMode(StickerPaletteMode.create)} /> + </> + ); render() { return ( <div className="sticker-palette" style={{ zIndex: 1000 }} onClick={e => e.stopPropagation()}> - {this._paletteMode === 'view' && ( - <> - <DocumentView - ref={r => (this._docView = r)} - Document={Doc.MyStickers} - addDocument={undefined} - addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={DocumentView.PinDoc} - containerViewPath={returnEmptyDocViewList} - styleProvider={DefaultStyleProvider} - removeDocument={returnFalse} - ScreenToLocalTransform={Transform.Identity} - PanelWidth={this.return170} - PanelHeight={this.return170} - renderDepth={0} - isContentActive={returnTrue} - focus={emptyFunction} - whenChildContentsActiveChanged={emptyFunction} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - /> - <Button text="Add" icon={<FontAwesomeIcon icon="square-plus" />} color={SettingsManager.userColor} onClick={() => this.setPaletteMode('create')} /> - </> - )} - {this._paletteMode === 'create' && ( - <> - <div className="palette-create"> - <input - className="palette-create-input" - aria-label="label-input" - id="new-label" - type="text" - value={this._userInput} - onChange={e => { - this.setUserInput(e.target.value); - }} - placeholder={this._showRegenerate ? '(Optional) Enter edits' : 'Enter item to draw'} - onKeyDown={this.handleKeyPress} - /> - <Button - style={{ alignSelf: 'flex-end' }} - tooltip={this._showRegenerate ? 'Regenerate' : 'Send'} - icon={this._isLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : this._showRegenerate ? <FontAwesomeIcon icon={'rotate'} /> : <AiOutlineSend />} - iconPlacement="right" - color={SettingsManager.userColor} - onClick={this.generateDrawings} - /> - </div> - <div className="palette-create-options"> - <div className="palette-color"> - Color - <Switch - sx={{ - '& .MuiSwitch-switchBase.Mui-checked': { - color: SettingsManager.userColor, - }, - '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { - backgroundColor: SettingsManager.userVariantColor, - }, - }} - defaultChecked={true} - value={this._opts.autoColor} - size="small" - onChange={() => this.setColor(!this._opts.autoColor)} - /> - </div> - <div className="palette-detail"> - Detail - <Slider - sx={{ - '& .MuiSlider-thumb': { - color: SettingsManager.userColor, - '&.Mui-focusVisible, &:hover, &.Mui-active': { - boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10`, - }, - }, - '& .MuiSlider-track': { - color: SettingsManager.userVariantColor, - }, - '& .MuiSlider-rail': { - color: SettingsManager.userColor, - }, - }} - style={{ width: '80%' }} - min={1} - max={10} - step={1} - size="small" - value={this._opts.complexity} - onChange={(e, val) => { - this.setDetail(val as number); - }} - valueLabelDisplay="auto" - /> - </div> - <div className="palette-size"> - Size - <Slider - sx={{ - '& .MuiSlider-thumb': { - color: SettingsManager.userColor, - '&.Mui-focusVisible, &:hover, &.Mui-active': { - boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}20`, - }, - }, - '& .MuiSlider-track': { - color: SettingsManager.userVariantColor, - }, - '& .MuiSlider-rail': { - color: SettingsManager.userColor, - }, - }} - style={{ width: '80%' }} - min={50} - max={500} - step={10} - size="small" - value={this._opts.size} - onChange={(e, val) => { - this.setSize(val as number); - }} - valueLabelDisplay="auto" - /> - </div> - </div> - <DocumentView - ref={r => (this._docCarouselView = r)} - Document={this._props.Document} - addDocument={undefined} - addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={DocumentView.PinDoc} - containerViewPath={returnEmptyDocViewList} - styleProvider={DefaultStyleProvider} - removeDocument={returnFalse} - ScreenToLocalTransform={Transform.Identity} - PanelWidth={this.return170} - PanelHeight={this.return170} - renderDepth={1} - isContentActive={returnTrue} - focus={emptyFunction} - whenChildContentsActiveChanged={emptyFunction} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - /> - <div className="palette-buttons"> - <Button text="Back" tooltip="Back to All Stickers" icon={<FontAwesomeIcon icon="reply" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(true)} /> - <div className="palette-save-reset"> - <Button tooltip="Save" icon={<FontAwesomeIcon icon="file-arrow-down" />} color={SettingsManager.userColor} onClick={this.saveDrawing} /> - <Button tooltip="Reset" icon={<FontAwesomeIcon icon="rotate-left" />} color={SettingsManager.userColor} onClick={() => this.resetPalette(false)} /> - </div> - </div> - </> - )} + {this._paletteMode === StickerPaletteMode.view ? this.renderPaletteView() : null} + {this._paletteMode === StickerPaletteMode.create ? this.renderPaletteCreate() : null} </div> ); } diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index a85606bc4..00114a3f9 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, isDark, Size, Type } from 'browndash-components'; +import { Button, IconButton, isDark, Size, Type } from '@dash/components'; import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 4dcb32f8b..12c5319b7 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -108,14 +108,14 @@ function registerEmbeddedBrowseRelativePathHandler(server: express.Express) { // detect search query and use default search engine res.redirect(req.headers.referer + 'corsProxy/' + encodeURIComponent('http://www.google.com' + relativeUrl)); } else { - res.end(); + res.status(404).json({ error: 'no such file or endpoint: try /home /logout /login' }); } }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any function proxyServe(req: any, requrl: string, response: any) { - // eslint-disable-next-line global-require, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires + // eslint-disable-next-line global-require, @typescript-eslint/no-require-imports const htmlBodyMemoryStream = new (require('memorystream'))(); let wasinBrFormat = false; const sendModifiedBody = () => { @@ -189,7 +189,6 @@ function proxyServe(req: any, requrl: string, response: any) { res.headers['x-permitted-cross-domain-policies'] = 'all'; res.headers['x-frame-options'] = ''; res.headers['content-security-policy'] = ''; - // eslint-disable-next-line no-multi-assign response.headers = response._headers = res.headers; }) .on('end', sendModifiedBody) @@ -247,6 +246,10 @@ export default async function InitializeServer(routeSetter: RouteSetter) { const app = buildWithMiddleware(express()); const compiler = webpack(config as webpack.Configuration); + // Default route + app.get('/', (req, res) => { + res.redirect(req.user ? '/home' : '/login'); //res.send('This is the default route.'); + }); // route table managed by express. routes are tested sequentially against each of these map rules. when a match is found, the handler is called to process the request app.use(wdm(compiler, { publicPath: config.output.publicPath })); app.use(whm(compiler)); @@ -259,7 +262,6 @@ export default async function InitializeServer(routeSetter: RouteSetter) { isRelease && !SSL.Loaded && SSL.exit(); routeSetter(new RouteManager(app, isRelease)); // this sets up all the regular supervised routes (things like /home, download/upload api's, pdf, search, session, etc) registerEmbeddedBrowseRelativePathHandler(app); // this allows renered web pages which internally have relative paths to find their content - isRelease && process.env.serverPort && (resolvedPorts.server = Number(process.env.serverPort)); const server = isRelease ? createServer(SSL.Credentials, app) : app; await new Promise<void>(resolve => { |