diff options
Diffstat (limited to 'src/client/views')
| -rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 51 | ||||
| -rw-r--r-- | src/client/views/FilterPanel.tsx | 352 | ||||
| -rw-r--r-- | src/client/views/StyleProvider.tsx | 10 | ||||
| -rw-r--r-- | src/client/views/nodes/FieldView.tsx | 4 |
4 files changed, 201 insertions, 216 deletions
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index ccde1de80..87dd5f45a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -298,16 +298,12 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( key={icon.toString()} size="sm" icon={icon} - onClick={e => { - if (name === 'tags'){ - (targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags)) + onClick={() => { + if (name === 'tags') { + targetDoc && (targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags); } else { - (targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels)) + targetDoc && (targetDoc[DocData].showLabels = !targetDoc[DocData].showLabels); } - - - - }} /> </div> @@ -315,31 +311,24 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); }; - - - return !targetDoc ? null : ( - <div className='documentButtonBar-icon'> - <div className="documentButtonBar-pinTypes" style = {{width: '40px'}}> - {metaBtn('tags', 'star')} - {metaBtn("keywords", 'id-card')} - </div> - - <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}> - <div - className="documentButtonBar-icon" - style={{ color: 'white' }} - onClick={() => { - // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags; - }} - > - - - <FontAwesomeIcon className="documentdecorations-icon" icon="tag" /> + <div className="documentButtonBar-icon"> + <div className="documentButtonBar-pinTypes" style={{ width: '40px' }}> + {metaBtn('tags', 'star')} + {metaBtn('keywords', 'id-card')} </div> - </Tooltip> - </div> + <Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}> + <div + className="documentButtonBar-icon" + style={{ color: 'white' }} + onClick={() => { + // targetDoc[DocData].showIconTags = !targetDoc[DocData].showIconTags; + }}> + <FontAwesomeIcon className="documentdecorations-icon" icon="tag" /> + </div> + </Tooltip> + </div> ); } @@ -519,4 +508,4 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx index 8133a4d0d..3795a4a6d 100644 --- a/src/client/views/FilterPanel.tsx +++ b/src/client/views/FilterPanel.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@mui/material'; import { action, computed, makeObservable, observable, ObservableMap } from 'mobx'; @@ -24,14 +23,186 @@ import { DocumentView } from './nodes/DocumentView'; import { ButtonType } from './nodes/FontIconBox/FontIconBox'; import { Handle, Tick, TooltipRail, Track } from './nodes/SliderBox-components'; import { ObservableReactComponent } from './ObservableReactComponent'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +interface HotKeyButtonProps { + hotKey: string; + selected?: Doc; +} + +/** + * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, + * title, and delete. + */ +const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey /*, selected */ }) => { + const state = useLocalObservable(() => ({ + isActive: false, + isEditing: false, + myHotKey: hotKey, + + toggleActive() { + this.isActive = !this.isActive; + }, + deactivate() { + this.isActive = false; + }, + startEditing() { + this.isEditing = true; + }, + stopEditing() { + this.isEditing = false; + }, + setHotKey(newHotKey: string) { + this.myHotKey = newHotKey; + }, + })); + + const panelRef = useRef<HTMLDivElement>(null); + const inputRef = useRef<HTMLInputElement>(null); + + const handleClick = () => { + state.toggleActive(); + }; + + const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); + const buttons = DocCast(Doc.UserDoc().myContextMenuBtns); + const filter = DocCast(buttons.Filter); + + /** + * The doc of the button in the context menu that corresponds to the current hotkey + * @returns + */ + const myHotKeyDoc = () => { + const hotKeyDocs = DocListCast(filter.data); + return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0]; + }; + + /** + * Removes a hotkey from list + */ + const removeHotKey = () => { + Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc()); + }; + + /** + * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new + * hotkey with the current icon + */ + const updateFromInput = undoable(() => { + const myDoc = myHotKeyDoc(); + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k))); + Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]); + Doc.UserDoc()[hotKey] = ''; + myDoc.title = state.myHotKey; + myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`; + }, ''); + + /** + * Deselects if the user clicks outside the button + * @param event + */ + const handleClickOutside = (event: MouseEvent) => { + if (panelRef.current && !panelRef.current.contains(event.target as Node)) { + state.deactivate(); + if (state.isEditing) { + state.stopEditing(); + + updateFromInput(); + } + } + }; + + useEffect(() => { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard'] as IconProp[]; + + /** + * Panel of icons the user can choose from to represent their tag + */ + const iconPanel = iconOpts.map((icon, i) => ( + <button + key={i} + onClick={undoable((e: React.MouseEvent) => { + e.stopPropagation; + Doc.UserDoc()[hotKey] = icon.toString(); + myHotKeyDoc().icon = icon.toString(); + }, '')} + className="icon-panel-button"> + <FontAwesomeIcon icon={icon} color={SnappingManager.userColor} /> + </button> + )); + + /** + * Actually renders the buttons + */ + + return ( + <div + className={`filterHotKey-button`} + onClick={e => { + e.stopPropagation(); + state.startEditing(); + setTimeout(() => inputRef.current?.focus(), 0); + }}> + <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}> + <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> + <button + type="button" + className="hotKey-icon" + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + handleClick(); + }}> + <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as IconProp} size="2xl" color={SnappingManager.userColor} /> + </button> + </Tooltip> + {state.isActive && <div className="icon-panel">{iconPanel}</div>} + </div> + {state.isEditing ? ( + <input + ref={inputRef} + type="text" + value={state.myHotKey.toUpperCase()} + onChange={e => state.setHotKey(e.target.value)} + onBlur={() => { + state.stopEditing(); + updateFromInput(); + }} + onKeyDown={e => { + if (e.key === 'Enter') { + state.stopEditing(); + updateFromInput(); + } + }} + className="hotkey-title-input" + /> + ) : ( + <p className="hotkey-title">{hotKey.toUpperCase()}</p> + )} + <button + className="hotKey-close" + onClick={(e: React.MouseEvent) => { + e.stopPropagation(); + Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); + removeHotKey(); + }}> + <FontAwesomeIcon icon={'x' as IconProp} color={SnappingManager.userColor} /> + </button> + </div> + ); +}); interface filterProps { Document: Doc; } @observer export class FilterPanel extends ObservableReactComponent<filterProps> { - @observable _selectedFacetHeaders = new Set<string>(); + // eslint-disable-next-line no-use-before-define public static Instance: FilterPanel; constructor(props: filterProps) { @@ -40,6 +211,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { FilterPanel.Instance = this; } + @observable _selectedFacetHeaders = new Set<string>(); /** * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection */ @@ -263,8 +435,8 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); // Selecting a button should make it so that the icon on the top filter panel becomes said icon - const buttons = hotKeys.map((hotKey, i) => ( - <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> + const buttons = hotKeys.map(hotKey => ( + <Tooltip key={hotKey} title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> <HotKeyIconButton hotKey={hotKey} selected={selected} /> </Tooltip> )); @@ -457,175 +629,3 @@ export class FilterPanel extends ObservableReactComponent<filterProps> { return undefined; } } - -interface HotKeyButtonProps { - hotKey: string; - selected?: Doc; -} - -/** - * Renders the buttons that correspond to each icon tag in the properties view. Allows users to change the icon, - * title, and delete. - */ -const HotKeyIconButton: React.FC<HotKeyButtonProps> = observer(({ hotKey, selected }) => { - const state = useLocalObservable(() => ({ - isActive: false, - isEditing: false, - myHotKey: hotKey, - - toggleActive() { - this.isActive = !this.isActive; - }, - deactivate() { - this.isActive = false; - }, - startEditing() { - this.isEditing = true; - }, - stopEditing() { - this.isEditing = false; - }, - setHotKey(newHotKey: string) { - this.myHotKey = newHotKey; - }, - })); - - const panelRef = useRef<HTMLDivElement>(null); - const inputRef = useRef<HTMLInputElement>(null); - - const handleClick = () => { - state.toggleActive(); - }; - - const hotKeys = StrListCast(Doc.UserDoc().myFilterHotKeyTitles); - const buttons = DocCast(Doc.UserDoc().myContextMenuBtns); - const filter = DocCast(buttons.Filter); - - /** - * The doc of the button in the context menu that corresponds to the current hotkey - * @returns - */ - const myHotKeyDoc = () => { - const hotKeyDocs = DocListCast(filter.data); - return hotKeyDocs.filter(k => StrCast(k.title) === hotKey)[0]; - }; - - /** - * Removes a hotkey from list - */ - const removeHotKey = () => { - Doc.RemoveDocFromList(filter, 'data', myHotKeyDoc()); - }; - - /** - * Deselects if the user clicks outside the button - * @param event - */ - const handleClickOutside = (event: MouseEvent) => { - if (panelRef.current && !panelRef.current.contains(event.target as Node)) { - state.deactivate(); - if (state.isEditing) { - state.stopEditing(); - - updateFromInput(); - } - } - }; - - /** - * Updates the list of hotkeys based on the users input. replaces the old title with the new one and then assigns this new - * hotkey with the current icon - */ - const updateFromInput = undoable(() => { - const myDoc = myHotKeyDoc(); - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.map(k => (k === hotKey ? state.myHotKey : k))); - Doc.UserDoc()[state.myHotKey] = StrCast(Doc.UserDoc()[hotKey]); - Doc.UserDoc()[hotKey] = ''; - myDoc.title = state.myHotKey; - myDoc.toolTip = `Click to toggle the ${state.myHotKey}'s group's visibility`; - }, ''); - - useEffect(() => { - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, []); - - const iconOpts = ['star', 'heart', 'bolt', 'satellite', 'palette', 'robot', 'lightbulb', 'highlighter', 'book', 'chalkboard']; - - /** - * Panel of icons the user can choose from to represent their tag - */ - const iconPanel = iconOpts.map((icon, i) => ( - <button - key={i} - onClick={undoable((e: React.MouseEvent) => { - e.stopPropagation; - Doc.UserDoc()[hotKey] = icon; - myHotKeyDoc().icon = icon; - }, '')} - className="icon-panel-button"> - <FontAwesomeIcon icon={icon as any} color={SnappingManager.userColor} /> - </button> - )); - - /** - * Actually renders the buttons - */ - - return ( - <div - className={`filterHotKey-button`} - onClick={e => { - e.stopPropagation(); - state.startEditing(); - setTimeout(() => inputRef.current?.focus(), 0); - }}> - <div className={`hotKey-icon-button ${state.isActive ? 'active' : ''}`} ref={panelRef}> - <Tooltip title={<div className="dash-tooltip">Click to customize this hotkey's icon</div>}> - <button - type="button" - className="hotKey-icon" - onClick={(e: React.MouseEvent) => { - e.stopPropagation(); - handleClick(); - }}> - <FontAwesomeIcon icon={Doc.UserDoc()[hotKey] as any} size="2xl" color={SnappingManager.userColor} /> - </button> - </Tooltip> - {state.isActive && <div className="icon-panel">{iconPanel}</div>} - </div> - {state.isEditing ? ( - <input - ref={inputRef} - type="text" - value={state.myHotKey.toUpperCase()} - onChange={e => state.setHotKey(e.target.value)} - onBlur={() => { - state.stopEditing(); - updateFromInput(); - }} - onKeyDown={e => { - if (e.key === 'Enter') { - state.stopEditing(); - updateFromInput(); - } - }} - className="hotkey-title-input" - /> - ) : ( - <p className="hotkey-title">{hotKey.toUpperCase()}</p> - )} - <button - className="hotKey-close" - onClick={(e: React.MouseEvent) => { - e.stopPropagation(); - Doc.UserDoc().myFilterHotKeyTitles = new List<string>(hotKeys.filter(k => k !== hotKey)); - removeHotKey(); - }}> - <FontAwesomeIcon icon={'x' as any} color={SnappingManager.userColor} /> - </button> - </div> - ); -}); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 13fef101a..eb434db40 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -326,7 +326,6 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & type={Type.TERT} dropdownType={DropdownType.CLICK} fillWidth - // eslint-disable-next-line react/no-unstable-nested-components iconProvider={() => <div className='styleProvider-filterShift'><FaFilter/></div>} closeOnSelect setSelectedVal={((dvValue: unknown) => { @@ -366,12 +365,9 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps & </Tooltip> ); }; - const tags = () => props?.DocumentView?.() ? <TagsView Views={[props.DocumentView()]}/> : null; - - const iconTags = () => { - if (doc && doc![DocData].showIconTags) - {return (<IconTagBox doc= {doc}></IconTagBox>)} - } + const tags = () => docView?.() ? <TagsView Views={[docView?.()]}/> : null; + const iconTags = () => doc?.[DocData].showIconTags ? <IconTagBox doc= {doc}/> : null; + return ( <> {paint()} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index c269c7bcb..683edba16 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/no-unused-prop-types */ -/* eslint-disable react/require-default-props */ import { Property } from 'csstype'; import { computed } from 'mobx'; import { observer } from 'mobx-react'; @@ -21,6 +19,7 @@ export type FocusFuncType = (doc: Doc, options: FocusViewOptions) => Opt<number> // eslint-disable-next-line no-use-before-define export type StyleProviderFuncType = ( doc: Opt<Doc>, + // eslint-disable-next-line no-use-before-define props: Opt<FieldViewProps>, property: string ) => @@ -65,6 +64,7 @@ export interface FieldViewSharedProps { containerViewPath?: () => DocumentView[]; fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _freeform_fitContentsToBox property on a Document isGroupActive?: () => string | undefined; // is this document part of a group that is active + // eslint-disable-next-line no-use-before-define setContentViewBox?: (view: ViewBoxInterface<FieldViewProps>) => void; // called by rendered field's viewBox so that DocumentView can make direct calls to the viewBox PanelWidth: () => number; PanelHeight: () => number; |
