diff options
Diffstat (limited to 'src/client/views/StyleProvider.tsx')
-rw-r--r-- | src/client/views/StyleProvider.tsx | 148 |
1 files changed, 101 insertions, 47 deletions
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 5f87f0697..5f16e0ebd 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,25 +1,29 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; +import { Shadows } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; -import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; -import { DashColor, emptyFunction, lightOrDark } from '../../Utils'; +import { DashColor, lightOrDark, Utils } from '../../Utils'; import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; -import { DocFocusOrOpen } from '../util/DocumentManager'; -import { ColorScheme } from '../util/SettingsManager'; +import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager'; +import { IsFollowLinkScript } from '../util/LinkFollower'; +import { LinkManager } from '../util/LinkManager'; +import { SelectionManager } from '../util/SelectionManager'; +import { ColorScheme, SettingsManager } from '../util/SettingsManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { TreeSort } from './collections/TreeView'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; import { MainView } from './MainView'; -import { DocumentViewProps } from './nodes/DocumentView'; +import { DocumentViewProps, OpenWhere } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; +import { KeyValueBox } from './nodes/KeyValueBox'; import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); -import { Shadows } from 'browndash-components'; -import { SelectionManager } from '../util/SelectionManager'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -31,12 +35,13 @@ export enum StyleProp { BorderRounding = 'borderRounding', // border radius of the document view Color = 'color', // foreground color of Document view items BackgroundColor = 'backgroundColor', // background color of a document view + FillColor = 'fillColor', // fill color of an ink stroke or shape WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note HideLinkButton = 'hideLinkButton', // hides the blue-dot link button. used when a document acts like a button - LinkSource = 'linkSource', // source document of a link -- used by LinkAnchorBox PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that + ShowCaption = 'showCaption', TitleHeight = 'titleHeight', // Height of Title area ShowTitle = 'showTitle', // whether to display a title on a Document (optional :hover suffix) JitterRotation = 'jitterRotation', // whether documents should be randomly rotated @@ -86,8 +91,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps const isAnchor = property.includes(':anchor'); const isAnnotated = property.includes(':annotated'); const isOpen = property.includes(':open'); + const boxBackground = property.includes(':box'); const fieldKey = props?.fieldKey ? props.fieldKey + '-' : isCaption ? 'caption-' : ''; - const isBackground = () => doc && BoolCast(doc._lockedPosition); + const lockedPosition = () => doc && BoolCast(doc._lockedPosition); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor); const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity); const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle); @@ -110,16 +116,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps allSorts[TreeSort.None] = { color: 'darkgray', label: '\u00A0\u00A0\u00A0' }; return allSorts; case StyleProp.Highlighting: - if (doc && !doc.noHighlighting) { - const highlightIndex = Doc.isBrushedHighlightedDegree(doc); - const highlightColor = ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex]; + if (doc && !doc.disableDocBrushing && !props?.disableDocBrushing) { + const selected = SelectionManager.Views().some(dv => dv.rootDoc === doc); + const highlightIndex = Doc.isBrushedHighlightedDegree(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0); + const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? 'black' : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex]; const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex]; - const excludeTypes = [DocumentType.FONTICON]; - let highlighting = !props?.disableDocBrushing && highlightIndex && !excludeTypes.includes(doc.type as any) && doc._viewType !== CollectionViewType.Linear; // bcz: hack to turn off highlighting onsidebar panel documents. need to flag a document as not highlightable in a more direct way - if (highlighting && props?.focus !== emptyFunction && StrCast(doc.title) !== '[pres element template]') { + if (highlightIndex) { return { highlightStyle, - highlightColor: highlightIndex !== Doc.DocBrushStatus.highlighted && SelectionManager.Views().some(dv => dv.rootDoc === doc) ? 'black' : highlightColor, + highlightColor, highlightIndex, highlightStroke: doc.type === DocumentType.INK, }; @@ -130,10 +135,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return undefined; case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? 'lightgrey' : 'dimgrey'; - // return doc = dragManager.dragDocument ? props.dragEffects.opacity??CastofOpacityonline94 : Cast()) - // same idea for background Color case StyleProp.Opacity: - return Cast(doc?._opacity, 'number', Cast(doc?.opacity, 'number', null)); + return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : Cast(doc?._opacity, 'number', Cast(doc?.opacity, 'number', null)); case StyleProp.HideLinkButton: return props?.hideLinkButton || (!selected && doc?.hideLinkButton); case StyleProp.FontSize: @@ -146,6 +149,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return ( (doc && !doc.presentationTargetDoc && + !props?.LayoutTemplateString?.includes(KeyValueBox.name) && props?.showTitle?.() !== '' && StrCast( doc._showTitle, @@ -158,26 +162,42 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps )) || '' ); + case StyleProp.FillColor: + return Cast(doc?._fillColor, 'string', Cast(doc?.fillColor, 'string', 'transparent')); case StyleProp.Color: if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY; const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); if (docColor) return docColor; const docView = props?.DocumentView?.(); - const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, 'backgroundColor'); + const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, StyleProp.BackgroundColor); if (!backColor) return undefined; return lightOrDark(backColor); case StyleProp.Hidden: - return BoolCast(doc?.hidden); + return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? false : BoolCast(doc?.hidden); case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.borderRounding, doc?._viewType === CollectionViewType.Pile ? '50%' : '')); case StyleProp.TitleHeight: return 15; case StyleProp.BorderPath: - return Doc.IsComicStyle(doc) && props?.renderDepth && doc?.type !== DocumentType.INK - ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, 0.08), width: 3 } - : { path: undefined, width: 0 }; + const borderPath = Doc.IsComicStyle(doc) && + props?.renderDepth && + doc?.type !== DocumentType.INK && { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, 0.08), width: 3 }; + return !borderPath + ? null + : { + clipPath: `path('${borderPath.path}')`, + jsx: ( + <div key="border2" className="documentView-customBorder" style={{ pointerEvents: 'none' }}> + <svg style={{ overflow: 'visible', height: '100%' }} viewBox={`0 0 ${props.PanelWidth()} ${props.PanelHeight()}`}> + <path d={borderPath.path} style={{ stroke: 'black', fill: 'transparent', strokeWidth: borderPath.width }} /> + </svg> + </div> + ), + }; case StyleProp.JitterRotation: return Doc.IsComicStyle(doc) ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0; + case StyleProp.ShowCaption: + return doc?._viewType === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._showCaption); case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || (doc?.type === DocumentType.RTF && !showTitle()?.includes('noMargin')) || @@ -197,7 +217,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); break; case DocumentType.FONTICON: - docColor = docColor || Colors.DARK_GRAY; + docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break; case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); @@ -214,7 +234,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps docColor = docColor || 'transparent'; break; case DocumentType.LABEL: - docColor = docColor || (doc.annotationOn !== undefined ? 'rgba(128, 128, 128, 0.18)' : undefined) || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); + docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); @@ -258,14 +278,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps case StyleProp.BoxShadow: { if (!doc || opacity() === 0 || doc.noShadow) return undefined; // if it's not visible, then no shadow) if (doc.boxShadow === 'standard') return Shadows.STANDARD_SHADOW; - if (doc?.isLinkButton && DocListCast(doc?.links).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em'); + if (IsFollowLinkScript(doc?.onClick) && LinkManager.Links(doc).length && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em'); switch (doc?.type) { case DocumentType.COL: return StrCast( doc?.boxShadow, doc?._viewType === CollectionViewType.Pile ? '4px 4px 10px 2px' - : isBackground() || doc?._isGroup || docProps?.LayoutTemplateString + : lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide) : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, '0.2vw 0.2vw 0.8vw')}` ); @@ -275,31 +295,68 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps default: return doc.z ? `#9c9396 ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow - : props?.ContainingCollectionDoc?._useClusters - ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent + : props?.docViewPath().lastElement()?.rootDoc._useClusters + ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent : NumCast(doc.group, -1) !== -1 - ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent - : isBackground() + ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(lockedPosition() ? 100 : 50) / (docProps?.NativeDimScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent + : lockedPosition() ? undefined // if it's a background & has a cluster color, make the shadow spread really big : StrCast(doc.boxShadow, ''); } } case StyleProp.PointerEvents: - if (MainView.Instance._exploreMode) return 'all'; + const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name); + if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.(); + if (MainView.Instance._exploreMode || doc?.unrendered) return isInk ? 'visiblePainted' : 'all'; if (doc?.pointerEvents) return StrCast(doc.pointerEvents); + if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents); if (props?.pointerEvents?.() === 'none') return 'none'; - const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name); - if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return 'none'; - if (!isInk) return props?.isDocumentActive?.() ? 'all' : undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active - return undefined; + if (opacity() === 0 || doc?.isInkMask) return 'none'; + if (props?.isDocumentActive?.()) return isInk ? 'visiblePainted' : 'all'; + return undefined; // fixes problem with tree view elements getting pointer events when the tree view is not active case StyleProp.Decorations: - if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) { - return doc && doc.pointerEvents === 'none' && isBackground() && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 ? ( - <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> - <FontAwesomeIcon icon={isBackground() ? 'lock' : 'unlock'} style={{ color: isBackground() ? 'red' : undefined }} size="lg" /> + const lock = () => { + if (props?.docViewPath().lastElement()?.rootDoc?._viewType === CollectionViewType.Freeform) { + return doc?.pointerEvents !== 'none' ? null : ( + <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> + <FontAwesomeIcon icon={'lock'} style={{ color: 'red' }} size="lg" /> + </div> + ); + } + }; + const filter = () => { + const showFilterIcon = + StrListCast(doc?._docFilters).length || StrListCast(doc?._docRangeFilters).length + ? '#18c718bd' //'hasFilter' + : docProps?.docFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || docProps?.docRangeFilters().length + ? 'orange' //'inheritsFilter' + : undefined; + return !showFilterIcon ? null : ( + <div className="styleProvider-filter" onClick={action(() => (SettingsManager.propertiesWidth = 250))}> + <FontAwesomeIcon icon={'filter'} size="lg" style={{ position: 'absolute', top: '1%', right: '1%', cursor: 'pointer', padding: 1, color: showFilterIcon, zIndex: 1 }} /> </div> - ) : null; - } + ); + }; + const audio = () => { + const audioAnnoState = (doc: Doc) => StrCast(doc.audioAnnoState, 'stopped'); + const audioAnnosCount = (doc: Doc) => StrListCast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations']).length; + if (!doc || props?.renderDepth === -1 || (!audioAnnosCount(doc) && audioAnnoState(doc) === 'stopped')) return null; + const audioIconColors: { [key: string]: string } = { recording: 'red', playing: 'green', stopped: 'blue' }; + return ( + <Tooltip title={<div>{StrListCast(doc[Doc.LayoutFieldKey(doc) + '-audioAnnotations-text']).lastElement()}</div>}> + <div className="styleProvider-audio" onPointerDown={() => DocumentManager.Instance.getFirstDocumentView(doc)?.docView?.playAnnotation()}> + <FontAwesomeIcon className="documentView-audioFont" style={{ color: audioIconColors[audioAnnoState(doc)] }} icon={'file-audio'} size="sm" /> + </div> + </Tooltip> + ); + }; + return ( + <> + {lock()} + {filter()} + {audio()} + </> + ); } } @@ -326,10 +383,7 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps return doc._viewType === CollectionViewType.Docking ? null : ( <> {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => { - doc.hidden = doc.hidden ? undefined : true; - if (!doc.hidden) { - DocFocusOrOpen(doc, props?.ContainingCollectionDoc); - } + DocFocusOrOpen(doc, { toggleTarget: true, willZoomCentered: true, zoomScale: 0 }, DocCast(doc?.context ?? doc?.annotationOn)); })} </> ); |