diff options
Diffstat (limited to 'src/client/views/StyleProvider.tsx')
-rw-r--r-- | src/client/views/StyleProvider.tsx | 174 |
1 files changed, 125 insertions, 49 deletions
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3bd4f5152..f7b9420a7 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,13 +1,16 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Shadows } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; -import { Doc, Opt } from '../../fields/Doc'; +import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; -import { DashColor, 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 { 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'; @@ -18,6 +21,10 @@ import { FieldViewProps } from './nodes/FieldView'; import { SliderBox } from './nodes/SliderBox'; import './StyleProvider.scss'; import React = require('react'); +import { KeyValueBox } from './nodes/KeyValueBox'; +import { listSpec } from '../../fields/Schema'; +import { AudioField } from '../../fields/URLField'; +import { Tooltip } from '@material-ui/core'; export enum StyleProp { TreeViewIcon = 'treeViewIcon', @@ -29,18 +36,21 @@ 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 BorderPath = 'customBorder', // border path for document view FontSize = 'fontSize', // size of text font - FontFamily = 'fontFamily', // size of text font + FontFamily = 'fontFamily', // font family of text + FontWeight = 'fontWeight', // font weight of text + Highlighting = 'highlighting', // border highlighting } function darkScheme() { @@ -82,9 +92,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 comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic'; - 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); @@ -106,29 +116,46 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps if (doc?._viewType === CollectionViewType.Freeform) allSorts[TreeSort.Zindex] = { color: 'green', label: 'z' }; allSorts[TreeSort.None] = { color: 'darkgray', label: '\u00A0\u00A0\u00A0' }; return allSorts; + case StyleProp.Highlighting: + 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]; + if (highlightIndex) { + return { + highlightStyle, + highlightColor, + highlightIndex, + highlightStroke: doc.type === DocumentType.INK, + }; + } + } + return undefined; case StyleProp.DocContents: 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?.isLinkButton || doc?.hideLinkButton)); + return props?.hideLinkButton || (!selected && doc?.hideLinkButton); case StyleProp.FontSize: - return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?.fontSize, StrCast(Doc.UserDoc().fontSize))); + return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._fontSize, StrCast(Doc.UserDoc().fontSize))); case StyleProp.FontFamily: - return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?.fontFamily, StrCast(Doc.UserDoc().fontFamily))); + return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._fontFamily, StrCast(Doc.UserDoc().fontFamily))); + case StyleProp.FontWeight: + return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._fontWeight, StrCast(Doc.UserDoc().fontWeight))); case StyleProp.ShowTitle: return ( (doc && !doc.presentationTargetDoc && + !props?.LayoutTemplateString?.includes(KeyValueBox.name) && props?.showTitle?.() !== '' && StrCast( doc._showTitle, props?.showTitle?.() || - (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) + (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.FUNCPLOT, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ? doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : remoteDocHeader @@ -136,26 +163,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 comicStyle() && 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 comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0; + 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')) || @@ -166,7 +209,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps : 0; case StyleProp.BackgroundColor: { if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY; - let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?._backgroundColor, StrCast(props?.Document.backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''))); + let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : '')); switch (doc?.type) { case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? '' : ''); @@ -175,7 +218,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); @@ -192,7 +235,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); @@ -220,7 +263,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps ? Colors.DARK_GRAY : Colors.LIGHT_GRAY // system docs (seen in treeView) get a grayish background : doc.annotationOn - ? '#00000015' // faint interior for collections on PDFs, images, etc + ? '#00000010' // faint interior for collections on PDFs, images, etc : doc?._isGroup ? undefined : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (darkScheme() ? Colors.BLACK : 'linear-gradient(#065fff, #85c1f9)')); @@ -234,17 +277,16 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return docColor; } case StyleProp.BoxShadow: { - if (!doc || opacity() === 0) return undefined; // if it's not visible, then no shadow) - - if (doc?.isLinkButton && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em'); - + 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 && 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')}` ); @@ -254,34 +296,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 && doc.type !== DocumentType.INK - ? `${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 - : NumCast(doc.group, -1) !== -1 && doc.type !== DocumentType.INK - ? `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() + : props?.ContainingCollectionDoc?._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 ${(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: + 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 'all'; - 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 && - (isBackground() || selected) && - !Doc.IsSystem(doc) && - (props?.renderDepth || 0) > 0 && - ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ? ( - <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> - <FontAwesomeIcon icon={isBackground() ? 'lock' : 'unlock'} style={{ color: isBackground() ? 'red' : undefined }} size="lg" /> + const lock = () => { + if (props?.ContainingCollectionDoc?._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()} + </> + ); } } |