diff options
author | Sophie Zhang <sophie_zhang@brown.edu> | 2023-09-18 17:40:01 -0400 |
---|---|---|
committer | Sophie Zhang <sophie_zhang@brown.edu> | 2023-09-18 17:40:01 -0400 |
commit | 013f25f01e729feee5db94900c61f4be4dd46869 (patch) | |
tree | 765dd5f2e06d6217ca79438e1098cefc8da627bf /src/client/views/StyleProvider.tsx | |
parent | f5e765adff1e7b32250eb503c9724a4ac99117f3 (diff) | |
parent | 84aa8806a62e2e957e8281d7d492139e3d8225f2 (diff) |
Merge branch 'master' into sophie-report-manager
Diffstat (limited to 'src/client/views/StyleProvider.tsx')
-rw-r--r-- | src/client/views/StyleProvider.tsx | 106 |
1 files changed, 68 insertions, 38 deletions
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index bbbad3690..1a4b8d7d2 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,9 +1,10 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { IconButton, Shadows, Size } from 'browndash-components'; +import { Dropdown, DropdownType, IconButton, IListItemProps, ListBox, ListItem, Popup, Shadows, Size, Type } from 'browndash-components'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; +import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs'; import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark, Utils } from '../../Utils'; @@ -14,21 +15,21 @@ 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 { TreeSort } from './collections/TreeSort'; import { Colors } from './global/globalEnums'; import { InkingStroke } from './InkingStroke'; -import { MainView } from './MainView'; -import { DocumentViewProps } from './nodes/DocumentView'; +import { DocumentView, DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; import { KeyValueBox } from './nodes/KeyValueBox'; import { SliderBox } from './nodes/SliderBox'; -import { BsArrowDown, BsArrowUp, BsArrowDownUp } from 'react-icons/bs' import './StyleProvider.scss'; import React = require('react'); +import { PropertiesView } from './PropertiesView'; +import { FaFilter } from 'react-icons/fa'; export enum StyleProp { - TreeViewIcon = 'treeViewIcon', - TreeViewSortings = 'treeViewSortings', // options for how to sort tree view items + TreeViewIcon = 'treeView_Icon', + TreeViewSortings = 'treeView_Sortings', // options for how to sort tree view items DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view Opacity = 'opacity', // opacity of the document view BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views @@ -44,7 +45,6 @@ export enum StyleProp { ShowCaption = 'layout_showCaption', TitleHeight = 'titleHeight', // Height of Title area ShowTitle = 'layout_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', // font family of text @@ -90,15 +90,16 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps const isCaption = property.includes(':caption'); const isAnchor = property.includes(':anchor'); const isAnnotated = property.includes(':annotated'); + const isInk = () => doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString; const isOpen = property.includes(':open'); const isEmpty = property.includes(':empty'); const boxBackground = property.includes(':box'); const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : ''; const lockedPosition = () => doc && BoolCast(doc._lockedPosition); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor); + const color = () => props?.styleProvider?.(doc, props, StyleProp.Color); const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity); const layout_showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle); - const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min); // prettier-ignore switch (property.split(':')[0]) { case StyleProp.TreeViewIcon: @@ -111,17 +112,17 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return Doc.toIcon(doc, isEmpty ? undefined : isOpen); case StyleProp.TreeViewSortings: const allSorts: { [key: string]: { color: string; icon: JSX.Element | string } | undefined } = {}; - allSorts[TreeSort.Down] = { color: Colors.MEDIUM_BLUE, icon: <BsArrowDown/> }; - allSorts[TreeSort.Up] = { color: 'crimson', icon: <BsArrowUp/> }; + allSorts[TreeSort.AlphaDown] = { color: Colors.MEDIUM_BLUE, icon: <BsArrowDown/> }; + allSorts[TreeSort.AlphaUp] = { color: 'crimson', icon: <BsArrowUp/> }; if (doc?._type_collection === CollectionViewType.Freeform) allSorts[TreeSort.Zindex] = { color: 'green', icon: 'Z' }; - allSorts[TreeSort.None] = { color: 'darkgray', icon: <BsArrowDownUp/> }; + allSorts[TreeSort.WhenAdded] = { color: 'darkgray', icon: <BsArrowDownUp/> }; return allSorts; case StyleProp.Highlighting: if (doc && (Doc.IsSystem(doc) || doc.type === DocumentType.FONTICON)) return undefined; if (doc && !doc.layout_disableBrushing && !props?.disableBrushing) { 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 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 { @@ -135,7 +136,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps return undefined; case StyleProp.DocContents:return undefined; case StyleProp.WidgetColor:return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? 'lightgrey' : 'dimgrey'; - case StyleProp.Opacity: return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.textInlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); + case StyleProp.Opacity: return props?.LayoutTemplateString?.includes(KeyValueBox.name) ? 1 : doc?.text_inlineAnnotations ? 0 : Cast(doc?._opacity, "number", Cast(doc?.opacity, 'number', null)); case StyleProp.HideLinkBtn:return props?.hideLinkButton || (!selected && doc?.layout_hideLinkButton); case StyleProp.FontSize: return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?._text_fontSize, StrCast(Doc.UserDoc().fontSize))); case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?._text_fontFamily, StrCast(Doc.UserDoc().fontFamily))); @@ -146,7 +147,8 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps case StyleProp.ShowTitle: return ( (doc && - !doc.presentationTargetDoc && + !props?.LayoutTemplateString && + !doc.presentation_targetDoc && !props?.LayoutTemplateString?.includes(KeyValueBox.name) && props?.layout_showTitle?.() !== '' && StrCast( @@ -161,15 +163,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps '' ); case StyleProp.Color: - if (MainView.Instance.LastButton === doc) return Doc.UserDoc().userBackgroundColor; - if (Doc.IsSystem(doc!)) return StrCast(Doc.UserDoc().userColor) - if (doc?.type === DocumentType.FONTICON) return Doc.UserDoc().userColor; + if (DocumentView.LastPressedSidebarBtn === doc) return SettingsManager.userBackgroundColor; + if (Doc.IsSystem(doc!)) return SettingsManager.userColor; + if (doc?.type === DocumentType.FONTICON) return SettingsManager.userColor; 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, StyleProp.BackgroundColor); - if (!backColor) return undefined; - return lightOrDark(backColor); + return backColor ? lightOrDark(backColor) : undefined; case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.layout_borderRounding, doc?._type_collection === CollectionViewType.Pile ? '50%' : '')); case StyleProp.BorderPath: @@ -188,8 +189,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps </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.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.NoteTaking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._type_collection as any) || (doc?.type === DocumentType.RTF && !layout_showTitle()?.includes('noMargin')) || @@ -199,7 +198,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps ? 15 : 0; case StyleProp.BackgroundColor: { - if (MainView.Instance.LastButton === doc) return StrCast(Doc.UserDoc().userColor); // hack to indicate active menu panel item + if (DocumentView.LastPressedSidebarBtn === doc) return SettingsManager.userColor; // hack to indicate active menu panel item let docColor: Opt<string> = StrCast(doc?.[fieldKey + '_backgroundColor'], StrCast(doc?._backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : '')); // prettier-ignore switch (doc?.type) { @@ -208,12 +207,10 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps case DocumentType.PRES: docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); break; case DocumentType.FONTICON: docColor = boxBackground ? undefined : docColor || Colors.DARK_GRAY; break; case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; - case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)'); break; case DocumentType.INK: docColor = doc?.stroke_isInkMask ? 'rgba(0,0,0,0.7)' : undefined; break; case DocumentType.EQUATION: docColor = docColor || 'transparent'; break; case DocumentType.LABEL: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; - case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : 'transparent'; break; case DocumentType.LINK: docColor = (isAnchor ? docColor : '') || 'transparent'; break; case DocumentType.IMG: case DocumentType.WEB: @@ -224,7 +221,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps case DocumentType.COL: if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break; docColor = docColor || (Doc.IsSystem(doc) - ? StrCast(Doc.UserDoc().userBackgroundColor) + ? SettingsManager.userBackgroundColor : doc.annotationOn ? '#00000010' // faint interior for collections on PDFs, images, etc : doc?._isGroup @@ -245,12 +242,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps switch (doc?.type) { case DocumentType.COL: return StrCast( - doc?.layout_borderRounding, + doc?.layout_boxShadow, doc?._type_collection === CollectionViewType.Pile ? '4px 4px 10px 2px' : lockedPosition() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide) - : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.layout_borderRounding, '0.2vw 0.2vw 0.8vw')}` + : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.layout_boxShadow, '0.2vw 0.2vw 0.8vw')}` ); case DocumentType.LABEL: @@ -268,14 +265,13 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps } } case StyleProp.PointerEvents: - const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name) && !props?.LayoutTemplateString; if (StrCast(doc?.pointerEvents) && !props?.LayoutTemplateString?.includes(KeyValueBox.name)) return StrCast(doc!.pointerEvents); // honor pointerEvents field (set by lock button usually) if it's not a keyValue view of the Doc if (docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.() !== undefined) return docProps?.DocumentView?.().ComponentView?.overridePointerEvents?.(); - if (MainView.Instance._exploreMode || doc?.layout_unrendered) return isInk ? 'visiblePainted' : 'all'; + if (DocumentView.ExploreMode || doc?.layout_unrendered) return isInk() ? 'visiblePainted' : 'all'; if (props?.contentPointerEvents) return StrCast(props.contentPointerEvents); if (props?.pointerEvents?.() === 'none') return 'none'; if (opacity() === 0) return 'none'; - if (props?.isDocumentActive?.()) return isInk ? 'visiblePainted' : 'all'; + 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: const lock = () => { @@ -288,25 +284,59 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps } }; const filter = () => { + const dashView = DocumentManager.Instance.getDocumentView(Doc.ActiveDashboard); const showFilterIcon = StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length - ? '#18c718bd' //'hasFilter' + ? 'green' // #18c718bd' //'hasFilter' : docProps?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || docProps?.childFiltersByRanges().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 className="styleProvider-filter"> + <Dropdown + type={Type.TERT} + dropdownType={DropdownType.CLICK} + fillWidth + iconProvider={(active:boolean) => <div className='styleProvider-filterShift'><FaFilter/></div>} + closeOnSelect={true} + setSelectedVal={ + action((dv) => { + (dv as any).select(false); + (SettingsManager.propertiesWidth = 250); + setTimeout(action(() => { + if (PropertiesView.Instance) { + PropertiesView.Instance.CloseAll(); + PropertiesView.Instance.openFilters = true; + } + })); + }) + } + size={Size.XSMALL} + width={15} + height={15} + title={showFilterIcon === 'green' ? + "This view is filtered. Click to view/change filters": + "this view inherits filters from one of its parents"} + color={SettingsManager.userColor} + background={showFilterIcon} + items={[ ...(dashView ? [dashView]: []), ...(props?.docViewPath?.()??[]), ...(props?.DocumentView?[props?.DocumentView?.()]:[])] + .filter(dv => StrListCast(dv.rootDoc.childFilters).length || StrListCast(dv.rootDoc.childRangeFilters).length) + .map(dv => ({ + text: StrCast(dv.rootDoc.title), + val: dv as any, + style: {color:SettingsManager.userColor, background:SettingsManager.userBackgroundColor}, + } as IListItemProps)) } + /> </div> ); }; 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 audioAnnosCount = (doc: Doc) => StrListCast(doc[Doc.LayoutFieldKey(doc) + '_audioAnnotations']).length; + if (!doc || props?.renderDepth === -1 || !audioAnnosCount(doc)) 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>}> + <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> @@ -324,7 +354,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps } export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, offIcon: IconProp, clickFunc?: () => void) { - const color = StrCast(Doc.UserDoc().userColor); + const color = SettingsManager.userColor; return ( <IconButton size={Size.XSMALL} |