diff options
-rw-r--r-- | src/client/documents/Documents.ts | 2 | ||||
-rw-r--r-- | src/client/util/CurrentUserUtils.ts | 2 | ||||
-rw-r--r-- | src/client/util/SettingsManager.tsx | 15 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 1 | ||||
-rw-r--r-- | src/client/views/EditableView.scss | 2 | ||||
-rw-r--r-- | src/client/views/EditableView.tsx | 17 | ||||
-rw-r--r-- | src/client/views/FilterPanel.tsx | 110 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 5 | ||||
-rw-r--r-- | src/client/views/global/globalScripts.ts | 15 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.scss | 6 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.tsx | 131 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.scss | 1 |
12 files changed, 201 insertions, 106 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 161aba6e1..5c913513a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -444,6 +444,8 @@ export class DocumentOptions { userBackgroundColor?: STRt = new StrInfo('background color associated with a Dash user (seen in header fields of shared documents)'); userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)'); } + +export const DocOptions = new DocumentOptions(); export namespace Docs { export let newAccount: boolean = false; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index dc988b04d..591bc7430 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -707,7 +707,7 @@ export class CurrentUserUtils { CollectionViewType.Grid, CollectionViewType.NoteTaking]), title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, { title: "Pin", icon: "map-pin", toolTip: "Pin View to Trail", btnType: ButtonType.ClickButton, expertMode: false, width: 30, scripts: { onClick: 'pinWithView(altKey)'}, funcs: {hidden: "IsNoneSelected()"}}, - { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, + { title: "Header", icon: "heading", toolTip: "Doc Titlebar Color", btnType: ButtonType.ColorButton, expertMode: true, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'} }, { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, expertMode: false, ignoreClick: true, width: 30, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}, funcs: {hidden: "IsNoneSelected()"}}, // Only when a document is selected { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode, true)'}, scripts: { onClick: '{ return toggleOverlay(_readOnly_); }'}}, // Only when floating document is selected in freeform { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, expertMode: true, toolType:CollectionViewType.Freeform, funcs: {hidden: '!SelectionManager_selectedDocType(self.toolType, self.expertMode)'}, width: 30, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx index dc852596f..f75322905 100644 --- a/src/client/util/SettingsManager.tsx +++ b/src/client/util/SettingsManager.tsx @@ -7,7 +7,7 @@ import { BsGoogle } from 'react-icons/bs'; import { FaFillDrip, FaPalette } from 'react-icons/fa'; import { Doc } from '../../fields/Doc'; import { DashVersion } from '../../fields/DocSymbols'; -import { BoolCast, Cast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, NumCast, StrCast } from '../../fields/Types'; import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { DocServer } from '../DocServer'; @@ -269,6 +269,19 @@ export class SettingsManager extends React.Component<{}> { size={Size.XSMALL} color={SettingsManager.userColor} /> + <Group formLabel="Title Height"> + <NumberDropdown + number={NumCast(Doc.UserDoc().headerHeight, 30)} + color={SettingsManager.userColor} + numberDropdownType={'slider'} + min={6} + max={60} + step={2} + type={Type.TERT} + unit={'px'} + setNumber={val => console.log('GOT: ' + (Doc.UserDoc().headerHeight = val))} + /> + </Group> </div> ); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index f40d2ae8b..9dafb12fb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -944,6 +944,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P key="rad" className="documentDecorations-borderRadius" style={{ + opacity: 0.5, background: `${this._isRounding ? Colors.MEDIUM_BLUE : SettingsManager.userColor}`, transform: `translate(${radiusHandleLocation ?? 0}px, ${(radiusHandleLocation ?? 0) + (this._showNothing ? 0 : this._titleHeight)}px)`, }} diff --git a/src/client/views/EditableView.scss b/src/client/views/EditableView.scss index f7c03caf9..27b260450 100644 --- a/src/client/views/EditableView.scss +++ b/src/client/views/EditableView.scss @@ -3,7 +3,7 @@ overflow-wrap: break-word; word-wrap: break-word; hyphens: auto; - overflow: auto; + overflow: hidden; height: 100%; min-width: 20; text-overflow: ellipsis; diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index ca4ffaf3a..ed7c544fc 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -283,11 +283,24 @@ export class EditableView extends React.Component<EditableProps> { <div className={`editableView-container-editing${this.props.oneLine ? '-oneLine' : ''}`} ref={this._ref} - style={{ display: this.props.display, textOverflow: this.props.overflow, minHeight: '10px', whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line', height: this.props.height, maxHeight: this.props.maxHeight }} + style={{ + display: this.props.display, // + textOverflow: this.props.overflow, + minHeight: '10px', + whiteSpace: this.props.oneLine ? 'nowrap' : 'pre-line', + height: this.props.height, + maxHeight: this.props.maxHeight, + }} //onPointerDown={this.stopPropagation} onClick={this.onClick} placeholder={this.props.placeholder}> - <span style={{ fontStyle: this.props.fontStyle, fontSize: this.props.fontSize }}>{this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()}</span> + <span + style={{ + fontStyle: this.props.fontStyle, + fontSize: this.props.fontSize, + }}> + {this.props.contents ? this.props.contents?.valueOf() : this.props.placeholder?.valueOf()} + </span> </div> ); } diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 0df88f970..cb5c9b085 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -7,7 +7,7 @@ import { CiCircleRemove } from 'react-icons/ci'; import Select from 'react-select'; import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../fields/Doc'; import { RichTextField } from '../../fields/RichTextField'; -import { DocumentOptions, FInfo } from '../documents/Documents'; +import { DocOptions, DocumentOptions, FInfo } from '../documents/Documents'; import { DocumentManager } from '../util/DocumentManager'; import { UserOptions } from '../util/GroupManager'; import { SearchUtil } from '../util/SearchUtil'; @@ -18,6 +18,7 @@ import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { SettingsManager } from '../util/SettingsManager'; import { Id } from '../../fields/FieldSymbols'; import { List } from '../../fields/List'; +import { emptyFunction } from '../../Utils'; interface filterProps { rootDoc: Doc; @@ -25,8 +26,6 @@ interface filterProps { @observer export class FilterPanel extends React.Component<filterProps> { - private _documentOptions: DocumentOptions = new DocumentOptions(); - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterPanel, fieldKey); } @@ -51,12 +50,10 @@ export class FilterPanel extends React.Component<filterProps> { if (targetDoc) { SearchUtil.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc)); } - console.log('this is all Docs' + Array.from(allDocs)); return Array.from(allDocs); } @computed get _allFacets() { - // trace(); const noviceReqFields = ['author', 'tags', 'text', 'type', LinkedTo]; const noviceLayoutFields: string[] = []; //["_layout_curPage"]; const noviceFields = [...noviceReqFields, ...noviceLayoutFields]; @@ -68,11 +65,8 @@ export class FilterPanel extends React.Component<filterProps> { .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode) .sort(); - // console.log('THIS IS HERE ' + Doc.UserDoc().color + 'space ' + Doc.UserDoc().color); noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1)); - console.log('this is novice fields ' + noviceFields + 'and this is sorted Keys ' + sortedKeys); - return [...noviceFields, ...sortedKeys]; } @@ -206,13 +200,7 @@ export class FilterPanel extends React.Component<filterProps> { */ @action - facetClick = (facetHeader: string) => { - // just when someone chooses a facet - - this._selectedFacetHeaders.add(facetHeader); - - return; - }; + facetClick = (facetHeader: string) => this._selectedFacetHeaders.add(facetHeader); @action sortingCurrentFacetValues = (facetHeader: string) => { @@ -260,57 +248,59 @@ export class FilterPanel extends React.Component<filterProps> { return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2)); }; - render() { - let filteredOptions: string[] = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())]; + @computed get fieldsDropdown() { + const filteredOptions = ['author', 'tags', 'text', 'acl-Guest', ...this._allFacets.filter(facet => facet[0] === facet.charAt(0).toUpperCase())]; - Object.entries(this._documentOptions).forEach((pair: [string, FInfo]) => { - if (pair[1].filterable) { - filteredOptions.push(pair[0]); - } - }); + Object.entries(DocOptions) + .filter(opts => opts[1].filterable) + .forEach((pair: [string, FInfo]) => filteredOptions.push(pair[0])); + const options = filteredOptions.map(facet => ({ value: facet, label: facet })); - let options = filteredOptions.map(facet => ({ value: facet, label: facet })); + return ( + <Select + styles={{ + control: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, + }), + placeholder: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, + }), + input: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: SettingsManager.userBackgroundColor, + }), + option: (baseStyles, state) => ({ + ...baseStyles, + color: SettingsManager.userColor, + background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor, + }), + menuList: (baseStyles, state) => ({ + ...baseStyles, + backgroundColor: SettingsManager.userBackgroundColor, + }), + }} + placeholder={'add a filter'} + options={options} + isMulti={false} + onChange={val => this.facetClick((val as UserOptions).value)} + onKeyDown={e => e.stopPropagation()} + //onMenuClose={onClose} + value={null} + closeMenuOnSelect={true} + /> + ); + } + render() { return ( <div className="filterBox-treeView"> <div className="filterBox-select"> - <div style={{ width: '100%' }}> - <Select - styles={{ - control: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, - }), - placeholder: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, - }), - input: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: SettingsManager.userBackgroundColor, - }), - option: (baseStyles, state) => ({ - ...baseStyles, - color: SettingsManager.userColor, - background: !state.isFocused ? SettingsManager.userBackgroundColor : SettingsManager.userVariantColor, - }), - menuList: (baseStyles, state) => ({ - ...baseStyles, - backgroundColor: SettingsManager.userBackgroundColor, - }), - }} - placeholder="Add a filter..." - options={options} - isMulti={false} - onChange={val => this.facetClick((val as UserOptions).value)} - onKeyDown={e => e.stopPropagation()} - value={null} - closeMenuOnSelect={true} - /> - </div> + <div style={{ width: '100%' }}>{this.fieldsDropdown}</div> {/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */} {/* <div className="filterBox-select-bool"> <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}> diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 48c96d064..5ff743a30 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -90,6 +90,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps const boxBackground = property.includes(':box'); const fieldKey = props?.fieldKey ? props.fieldKey + '_' : isCaption ? 'caption_' : ''; const lockedPosition = () => doc && BoolCast(doc._lockedPosition); + const titleHeight = () => props?.styleProvider?.(doc, props, StyleProp.TitleHeight); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor); const color = () => props?.styleProvider?.(doc, props, StyleProp.Color); const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity); @@ -137,7 +138,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps case StyleProp.FontWeight: return StrCast(doc?.[fieldKey + 'fontWeight'], StrCast(doc?._text_fontWeight, StrCast(Doc.UserDoc().fontWeight))); case StyleProp.FillColor: return StrCast(doc?._fillColor, StrCast(doc?.fillColor, 'transparent')); case StyleProp.ShowCaption:return doc?._type_collection === CollectionViewType.Carousel || props?.hideCaptions ? undefined : StrCast(doc?._layout_showCaption); - case StyleProp.TitleHeight:return 15; + case StyleProp.TitleHeight:return (props?.ScreenToLocalTransform().Scale ?? 1)*(props?.NativeDimScaling?.()??1) * NumCast(Doc.UserDoc().headerHeight,30) case StyleProp.ShowTitle: return ( (doc && @@ -189,7 +190,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps doc?.type === DocumentType.LABEL) && layout_showTitle() && !StrCast(doc?.layout_showTitle).includes(':hover') - ? 15 + ? titleHeight() : 0; case StyleProp.BackgroundColor: { if (DocumentView.LastPressedSidebarBtn === doc) return SettingsManager.userColor; // hack to indicate active menu panel item diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index d8ed93bd7..ced11447c 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -68,11 +68,18 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b // toggle: Set overlay status of selected document ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) { if (checkResult) { - return Doc.SharingDoc().headingColor; + return SelectionManager.Views().length ? StrCast(SelectionManager.Docs().lastElement().layout_headingColor) : Doc.SharingDoc().headingColor; + } + if (SelectionManager.Views().length) { + SelectionManager.Docs().forEach(doc => { + Doc.GetProto(doc).layout_headingColor = color; + doc.layout_showTitle = color === 'transparent' ? undefined : StrCast(doc.layout_showTitle, 'title'); + }); + } else { + Doc.SharingDoc().headingColor = undefined; + Doc.GetProto(Doc.SharingDoc()).headingColor = color; + Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date'); } - Doc.SharingDoc().headingColor = undefined; - Doc.GetProto(Doc.SharingDoc()).headingColor = color; - Doc.UserDoc().layout_showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().layout_showTitle, 'author_date'); }); // toggle: Set overlay status of selected document diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index b25540dd3..874723895 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -24,6 +24,7 @@ width: 100%; height: 100%; border-radius: inherit; + overflow: hidden; // need this so that title will be clipped when borderRadius is set // transition: outline 0.3s linear; // background: $white; //overflow: hidden; @@ -118,7 +119,7 @@ display: flex; justify-content: center; align-items: center; - + .sharingIndicator { height: 30px; width: 30px; @@ -185,6 +186,7 @@ text-overflow: ellipsis; white-space: pre; position: absolute; + display: flex; // this allows title field dropdown to be inline with editable title } .documentView-titleWrapper-hover { @@ -214,7 +216,7 @@ .documentView-node:hover { > .documentView-styleWrapper { > .documentView-titleWrapper-hover { - display: inline-block; + display: flex; } // > .documentView-contentsView { // opacity: 0.5; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index c2355e4f7..4a43ffe3e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -17,7 +17,7 @@ import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { emptyFunction, isTargetChildOf as isParentOf, lightOrDark, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { DocServer } from '../../DocServer'; -import { Docs, DocUtils } from '../../documents/Documents'; +import { DocOptions, Docs, DocUtils, FInfo } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; import { DictationManager } from '../../util/DictationManager'; @@ -54,6 +54,8 @@ import { PinProps, PresBox } from './trails/PresBox'; import React = require('react'); import { KeyValueBox } from './KeyValueBox'; import { LinkBox } from './LinkBox'; +import { FilterPanel } from '../FilterPanel'; +import { Dropdown, DropdownType, Type } from 'browndash-components'; const { Howl } = require('howler'); interface Window { @@ -143,7 +145,7 @@ export interface DocComponentView { annotationKey?: string; getTitle?: () => string; getCenter?: (xf: Transform) => { X: number; Y: number }; - screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?:{X:number, Y:number} }; + screenBounds?: () => { left: number; top: number; right: number; bottom: number; center?: { X: number; Y: number } }; ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number }; snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number }; @@ -622,7 +624,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps if (this.props.Document === Doc.ActiveDashboard) { e.stopPropagation(); e.preventDefault(); - alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.'); + alert( + (e.target as any)?.closest?.('*.lm_content') + ? "You can't perform this move most likely because you didn't drag the document's title bar to enable embedding in a different document." + : 'Linking to document tabs not yet supported. Drop link on document content.' + ); return true; } const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData; @@ -1068,6 +1074,41 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps }; captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption'); + @observable _changingTitleField = false; + @observable _dropDownInnerWidth = 0; + fieldsDropdown = (inputOptions: string[], dropdownWidth: number, placeholder: string, onChange: (val: string | number) => void, onClose: () => void) => { + const filteredOptions = new Set(inputOptions); + const scaling = this.titleHeight / 30; /* height of Dropdown */ + Object.entries(DocOptions) + .filter(opts => opts[1].filterable) + .forEach((pair: [string, FInfo]) => filteredOptions.add(pair[0])); + filteredOptions.add(StrCast(this.layoutDoc.layout_showTitle)); + const options = Array.from(filteredOptions) + .filter(f => f) + .map(facet => ({ val: facet, text: facet })); + return ( + <div style={{ width: dropdownWidth }}> + <div + ref={action((r: any) => r && (this._dropDownInnerWidth = Number(getComputedStyle(r).width.replace('px', ''))))} + onPointerDown={action(e => (this._changingTitleField = true))} + style={{ width: 'max-content', transformOrigin: 'left', transform: `scale(${scaling})` }}> + <Dropdown + activeChanged={action(isOpen => !isOpen && (this._changingTitleField = false))} + selectedVal={placeholder} + setSelectedVal={onChange} + color={SettingsManager.userColor} + background={SettingsManager.userVariantColor} + type={Type.TERT} + closeOnSelect={true} + dropdownType={DropdownType.SELECT} + items={options} + width={100} + fillWidth + /> + </div> + </div> + ); + }; @computed get innards() { TraceMobx(); const showTitle = this.layout_showTitle?.split(':')[0]; @@ -1095,8 +1136,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps </div> ); const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc; - const background = StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor)); + const background = StrCast( + this.layoutDoc.layout_headingColor, + StrCast(SharingManager.Instance.users.find(u => u.user.email === this.dataDoc.author)?.sharingDoc.headingColor, StrCast(this.layoutDoc.layout_headingColor, StrCast(Doc.SharingDoc().headingColor, SettingsManager.userBackgroundColor))) + ); + const dropdownWidth = this._titleRef.current?._editing || this._changingTitleField ? Math.max(10, (this._dropDownInnerWidth * this.titleHeight) / 30) : 0; const sidebarWidthPercent = +StrCast(this.layoutDoc.layout_sidebarWidthPercent).replace('%', ''); + // displays a 'title' at the top of a document. The title contents default to the 'title' field, but can be changed to one or more fields by + // setting layout_showTitle using the format: field1[;field2[...][:hover]] + // from the UI, this is done by clicking the title field and prefixin the format with '#'. eg., #field1[;field2;...][:hover] const titleView = !showTitle ? null : ( <div className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`} @@ -1104,39 +1152,58 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps style={{ position: this.headerMargin ? 'relative' : 'absolute', height: this.titleHeight, - width: !this.headerMargin ? `calc(${sidebarWidthPercent || 100}% - 18px)` : (sidebarWidthPercent || 100) + '%', // leave room for annotation button + width: 100 - sidebarWidthPercent + '%', color: background === 'transparent' ? SettingsManager.userColor : lightOrDark(background), background, pointerEvents: (!this.disableClickScriptFunc && this.onClickHandler) || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined, }}> - <EditableView - ref={this._titleRef} - contents={showTitle - .split(';') - .map(field => field.trim()) - .map(field => targetDoc[field]?.toString()) - .join('\\')} - display={'block'} - fontSize={10} - GetValue={() => { - return showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle; - }} - SetValue={undoBatch((input: string) => { - if (input?.startsWith('#')) { - if (this.rootDoc.layout_showTitle) { - this.rootDoc._layout_showTitle = input?.substring(1) ? input.substring(1) : undefined; - } else if (!this.props.layout_showTitle) { - Doc.UserDoc().layout_showTitle = input?.substring(1) ? input.substring(1) : 'author_date'; + {!dropdownWidth + ? null + : this.fieldsDropdown( + [], + dropdownWidth, + StrCast(this.layoutDoc.layout_showTitle).split(':')[0], + action((field: string | number) => { + if (this.rootDoc.layout_showTitle) { + this.rootDoc._layout_showTitle = field; + } else if (!this.props.layout_showTitle) { + Doc.UserDoc().layout_showTitle = field; + } + this._changingTitleField = false; + }), + action(() => (this._changingTitleField = false)) + )} + <div + style={{ + width: `calc(100% - ${dropdownWidth}px)`, + minWidth: '100px', + color: this._titleRef.current?._editing || this._changingTitleField ? 'black' : undefined, + background: this._titleRef.current?._editing || this._changingTitleField ? 'yellow' : undefined, + }}> + <EditableView + ref={this._titleRef} + contents={showTitle + .split(';') + .map(field => targetDoc[field.trim()]?.toString()) + .join(' \\ ')} + display="block" + oneLine={true} + fontSize={(this.titleHeight / 15) * 10} + GetValue={() => (showTitle.split(';').length !== 1 ? '#' + showTitle : Field.toKeyValueString(this.rootDoc, showTitle.split(';')[0]))} + SetValue={undoBatch((input: string) => { + if (input?.startsWith('#')) { + if (this.rootDoc.layout_showTitle) { + this.rootDoc._layout_showTitle = input?.substring(1); + } else if (!this.props.layout_showTitle) { + Doc.UserDoc().layout_showTitle = input?.substring(1) ?? 'author_date'; + } + } else if (showTitle && !showTitle.includes('Date') && showTitle !== 'author') { + KeyValueBox.SetField(targetDoc, showTitle, input); } - } else { - var value = input.replace(new RegExp(showTitle + '='), '') as string | number; - if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value); - if (showTitle.includes('Date') || showTitle === 'author') return true; - Doc.SetInPlace(targetDoc, showTitle, value, true); - } - return true; - })} - /> + return true; + })} + /> + </div> </div> ); return this.props.hideTitle || (!showTitle && !this.layout_showCaption) ? ( diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 6765e1dea..818c0cbe7 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -37,7 +37,6 @@ audiotag:hover { background: inherit; padding: 0; border-width: 0px; - border-radius: inherit; border-color: $medium-gray; box-sizing: border-box; background-color: inherit; |