From 29a1fe16297c99ddbed7974b7c602294da9a311d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 25 Mar 2025 22:40:30 -0400 Subject: fixes to components so that things highlight reasonably in different skins. fixed color picker alternate selection. --- .../components/src/components/Button/Button.scss | 12 +- .../components/src/components/Button/Button.tsx | 345 ++++++++++----------- .../src/components/ColorPicker/ColorPicker.tsx | 44 ++- .../src/components/Dropdown/Dropdown.tsx | 13 +- .../components/DropdownSearch/DropdownSearch.tsx | 190 +++++------- .../src/components/EditableText/EditableText.tsx | 309 +++++++++--------- .../src/components/IconButton/IconButton.scss | 17 +- .../src/components/IconButton/IconButton.tsx | 27 +- .../src/components/ListItem/ListItem.tsx | 2 +- .../src/components/MultiToggle/MultiToggle.tsx | 143 +++++---- .../components/NumberDropdown/NumberDropdown.tsx | 46 ++- .../src/components/NumberInput/NumberInput.tsx | 137 ++++---- packages/components/src/components/Popup/Popup.tsx | 8 +- .../components/src/components/Slider/Slider.tsx | 16 +- .../components/src/components/Toggle/Toggle.tsx | 309 +++++++++--------- packages/components/src/global/globalEnums.tsx | 67 ++-- packages/components/src/global/globalUtils.tsx | 163 +++++----- 17 files changed, 919 insertions(+), 929 deletions(-) (limited to 'packages/components/src') diff --git a/packages/components/src/components/Button/Button.scss b/packages/components/src/components/Button/Button.scss index bbe2e2470..b2fb48344 100644 --- a/packages/components/src/components/Button/Button.scss +++ b/packages/components/src/components/Button/Button.scss @@ -53,7 +53,7 @@ &.inactive { &:hover { .background { - filter: opacity(0) !important; + filter: opacity(0); } } } @@ -63,13 +63,13 @@ filter: opacity(0); &.active { - filter: opacity(0.2) !important; + filter: opacity(0.5); } } &:hover { .background { - filter: opacity(0.2); + filter: opacity(0.3); } } } @@ -79,7 +79,7 @@ filter: opacity(0); &.active { - filter: opacity(0.2) !important; + filter: opacity(0.2); } } @@ -96,12 +96,12 @@ } .background { - filter: opacity(1) !important; + filter: opacity(1); } &:hover { .background { - filter: brightness(0.8); + filter: opacity(0.3); } } } diff --git a/packages/components/src/components/Button/Button.tsx b/packages/components/src/components/Button/Button.tsx index a91c74a4c..885403640 100644 --- a/packages/components/src/components/Button/Button.tsx +++ b/packages/components/src/components/Button/Button.tsx @@ -1,195 +1,188 @@ -import { Tooltip } from '@mui/material' -import React from 'react' -import { Alignment, IGlobalProps, Placement, Type , getFormLabelSize } from '../../global' -import { Colors, Size } from '../../global/globalEnums' -import { getFontSize, getHeight, isDark } from '../../global/globalUtils' -import { IconButton } from '../IconButton' -import './Button.scss' +import { Tooltip } from '@mui/material'; +import React from 'react'; +import { Alignment, IGlobalProps, Placement, Type, getFormLabelSize } from '../../global'; +import { Colors, Size } from '../../global/globalEnums'; +import { getFontSize, getHeight } from '../../global/globalUtils'; +import { IconButton } from '../IconButton'; +import './Button.scss'; export interface IButtonProps extends IGlobalProps { - onClick?: (event: React.MouseEvent) => void - onDoubleClick?: (event: React.MouseEvent) => void - type?: Type - active?: boolean + onClick?: (event: React.MouseEvent) => void; + onDoubleClick?: (event: React.MouseEvent) => void; + type?: Type; + active?: boolean; - // Content - text?: string - icon?: JSX.Element | string + // Content + text?: string; + icon?: JSX.Element | string; - // Additional stylization - iconPlacement?: Placement - color?: string - colorPicker?: string, - uppercase?: boolean, - align?: Alignment + // Additional stylization + iconPlacement?: Placement; + color?: string; + colorPicker?: string; + uppercase?: boolean; + align?: Alignment; + filter?: string; } export const Button = (props: IButtonProps) => { - const { - text, - icon, - onClick, - onDoubleClick, - onPointerDown, - active, - height, - inactive, - type = Type.PRIM, - label, - uppercase = false, - iconPlacement = 'right', - size = Size.SMALL, - color = Colors.MEDIUM_BLUE, - background, - style, - tooltip, - tooltipPlacement = 'top', - colorPicker, - formLabel, - formLabelPlacement, - fillWidth, - align = fillWidth ? 'flex-start' : 'center' - } = props + const { + text, + icon, + onClick, + onDoubleClick, + onPointerDown, + active, + height, + inactive, + type = Type.PRIM, + filter, + uppercase = false, + iconPlacement = 'right', + size = Size.SMALL, + color = Colors.MEDIUM_BLUE, + background, + style, + tooltip, + tooltipPlacement = 'top', + colorPicker, + formLabel, + formLabelPlacement, + fillWidth, + align = fillWidth ? 'flex-start' : 'center', + } = props; - if (!text) { - return - } - - /** - * Pointer down - * @param e - */ - const handlePointerDown = (e: React.PointerEvent) => { - - if (!inactive && onPointerDown) { - e.stopPropagation(); - e.preventDefault(); - onPointerDown(e) + if (!text) { + return ; } - } - /** - * In the event that there is a single click - * @param e - */ - const handleClick = (e: React.MouseEvent) => { - if (!inactive && onClick) { - e.stopPropagation(); - e.preventDefault(); - onClick(e) - } - } + /** + * Pointer down + * @param e + */ + const handlePointerDown = (e: React.PointerEvent) => { + if (!inactive && onPointerDown) { + e.stopPropagation(); + e.preventDefault(); + onPointerDown(e); + } + }; - /** - * Double click - * @param e - */ - const handleDoubleClick = (e: React.MouseEvent) => { - if (!inactive && onDoubleClick){ - e.stopPropagation(); - e.preventDefault(); - onDoubleClick(e) - } - } + /** + * In the event that there is a single click + * @param e + */ + const handleClick = (e: React.MouseEvent) => { + if (!inactive && onClick) { + e.stopPropagation(); + e.preventDefault(); + onClick(e); + } + }; - const getBorderColor = (): Colors | string | undefined => { - switch(type){ - case Type.PRIM: - return undefined; - case Type.SEC: - if (colorPicker) return colorPicker; - return color; - case Type.TERT: - if (colorPicker) return colorPicker; - if (active) return color; - else return color; - } - } + /** + * Double click + * @param e + */ + const handleDoubleClick = (e: React.MouseEvent) => { + if (!inactive && onDoubleClick) { + e.stopPropagation(); + e.preventDefault(); + onDoubleClick(e); + } + }; - const getColor = (): Colors | string | undefined => { - if (color && background) return color; - switch(type){ - case Type.PRIM: - if (colorPicker) return colorPicker - return color; - case Type.SEC: - if (colorPicker) return colorPicker - return color; - case Type.TERT: - if (colorPicker) { - if (isDark(colorPicker)) return Colors.WHITE; - else return Colors.BLACK + const getBorderColor = (): Colors | string | undefined => { + switch (type) { + case Type.PRIM: + return undefined; + case Type.SEC: + if (colorPicker) return colorPicker; + return color; + case Type.TERT: + return color; } - if (isDark(color)) return Colors.WHITE; - else return Colors.BLACK - } - } + }; - const getBackground = (): Colors | string | undefined => { - if (background) return background; - switch(type) { - case Type.PRIM: - if (colorPicker) return colorPicker - return color; - case Type.SEC: - if (colorPicker) return colorPicker - return color; - case Type.TERT: - if (colorPicker) return colorPicker - else return color - } - } + const getColor = (): Colors | string | undefined => { + if (color && background) return color; + switch (type) { + case Type.PRIM: + case Type.SEC: + if (colorPicker) return colorPicker; + return color; + case Type.TERT: + return ''; + } + }; - const defaultProperties: React.CSSProperties = { - height: getHeight(height, size), - minHeight: getHeight(height, size), - width: fillWidth ? '100%' : 'fit-content', - justifyContent: align ? align : undefined, - padding: fillWidth && align === 'center' ? 0 : undefined, - fontWeight: 500, - fontSize: getFontSize(size), - fontFamily: 'sans-serif', - textTransform: uppercase ? 'uppercase' : undefined, - borderColor: getBorderColor(), - color: getColor(), - } + const getBackground = (): Colors | string | undefined => { + if (background) return background; + switch (type) { + case Type.PRIM: + case Type.SEC: + if (colorPicker) return colorPicker; + return color; + case Type.TERT: + } + }; - const backgroundProperties: React.CSSProperties = { - background: getBackground() - } + const defaultProperties: React.CSSProperties = { + height: getHeight(height, size), + minHeight: getHeight(height, size), + width: fillWidth ? '100%' : 'fit-content', + justifyContent: align ? align : undefined, + padding: fillWidth && align === 'center' ? 0 : undefined, + fontWeight: 500, + fontSize: getFontSize(size), + fontFamily: 'sans-serif', + textTransform: uppercase ? 'uppercase' : undefined, + borderColor: getBorderColor(), + color: getColor(), + }; - const button: JSX.Element = ( - -
-
- {iconPlacement == 'left' && icon ?
{icon}
: null} - {text} - {iconPlacement == 'right' && icon ?
{icon}
: null} -
-
-
- - ) + const backgroundProperties: React.CSSProperties = { + background: getBackground(), + filter, + }; - return ( - formLabel ? -
-
{formLabel}
- {button} -
- : - button - ) -} + const button: JSX.Element = ( + +
+
+ {iconPlacement == 'left' && icon ? ( +
+ {icon} +
+ ) : null} + {text} + {iconPlacement == 'right' && icon ? ( +
+ {icon} +
+ ) : null} +
+
+
+ + ); + + return formLabel ? ( +
+
+ {formLabel} +
+ {button} +
+ ) : ( + button + ); +}; diff --git a/packages/components/src/components/ColorPicker/ColorPicker.tsx b/packages/components/src/components/ColorPicker/ColorPicker.tsx index 632b470f0..72e02d955 100644 --- a/packages/components/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/components/src/components/ColorPicker/ColorPicker.tsx @@ -1,5 +1,5 @@ import React, { useRef, useState } from 'react'; -import { GithubPicker, ChromePicker, BlockPicker, SliderPicker, SketchPicker } from 'react-color'; +import { GithubPicker, ChromePicker, BlockPicker, SliderPicker, SketchPicker, ColorResult } from 'react-color'; import { IGlobalProps, Size, Type, getFormLabelSize } from '../../global'; import { Button } from '../Button'; import { IconButton } from '../IconButton'; @@ -16,12 +16,12 @@ export interface IColorPickerProps extends IGlobalProps { colorPickerType?: ColorPickerType; defaultPickerType?: ColorPickerType; selectedColor?: string; - setSelectedColor: (color: any) => unknown; - setFinalColor: (color: any) => unknown; + setSelectedColor: (color: string) => unknown; + setFinalColor: (color: string) => unknown; } export const ColorPicker = (props: IColorPickerProps) => { - const [selectedColorLoc, setSelectedColorLoc] = useState(); + const [selectedColorLoc, setSelectedColorLoc] = useState(); const { defaultPickerType, text, @@ -31,6 +31,7 @@ export const ColorPicker = (props: IColorPickerProps) => { size = Size.SMALL, type = Type.TERT, icon, + background, selectedColor = selectedColorLoc, setSelectedColor = setSelectedColorLoc, setFinalColor = setSelectedColorLoc, @@ -40,32 +41,25 @@ export const ColorPicker = (props: IColorPickerProps) => { } = props; const [isOpen, setOpen] = useState(false); const [pickerSelectorOpen, setPickerSelectorOpen] = useState(false); - const decimalToHexString = (number: number) => { + const decimalToHexString = (numberIn: number) => { + let number = numberIn; if (number < 0) { number = 0xffffffff + number + 1; } return (number < 16 ? '0' : '') + number.toString(16).toUpperCase(); }; - const colorString = (color: any) => { - return color.hex === 'transparent' ? color.hex : color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff'); - }; - const onChange = (color: any) => { - setSelectedColor(colorString(color) as any); - }; - const onChangeComplete = (color: any) => { - setFinalColor(colorString(color) as any); - }; + const colorString = (c: ColorResult) => (c.hex === 'transparent' ? c.hex : c.hex + (c.rgb.a ? decimalToHexString(Math.round(c.rgb.a * 255)) : 'ff')); + const onChange = (c: ColorResult) => setSelectedColor(colorString(c)); + const onChangeComplete = (c: ColorResult) => setFinalColor(colorString(c)); const [picker, setPicker] = useState(defaultPickerType ?? 'Classic'); const toggleRef = useRef(null); const getToggle = () => (
- {icon && !text ? ( - - ) : text ? ( -
); @@ -92,14 +86,12 @@ export const ColorPicker = (props: IColorPickerProps) => { ); } // prettier-ignore }; - const openChanged = (isOpen: boolean) => setPickerSelectorOpen(isOpen); + const openChanged = (state: boolean) => setPickerSelectorOpen(state); const getPopup = (): JSX.Element => { if (colorPickerType) { return getColorPicker(colorPickerType); } else { - // Todo: this would be much easier if the selectedColor was a Color, not a string. - const newColor = selectedColor === 'transparent' ? 'white' : selectedColor?.startsWith('#') ? selectedColor.substring(0, 7) : selectedColor?.startsWith('rgba') ? selectedColor?.replace(/,[0-9]*\)/, '1)') : selectedColor; return (
{ val: item, }; })} + background={background} activeChanged={openChanged} placement={'right'} - color={newColor} + color={color} type={Type.PRIM} dropdownType={DropdownType.SELECT} selectedVal={picker} @@ -126,7 +119,7 @@ export const ColorPicker = (props: IColorPickerProps) => { const popupContainsPt = (x: number, y: number) => { const rect = toggleRef.current?.getBoundingClientRect(); - return rect && rect.left < x && rect.top < y && rect.right > x && rect.bottom > y; + return pickerSelectorOpen || (rect && rect.left < x && rect.top < y && rect.right > x && rect.bottom > y) ? true : false; }; const colorPicker: JSX.Element = ( @@ -138,6 +131,7 @@ export const ColorPicker = (props: IColorPickerProps) => { tooltip={tooltip} size={size} color={selectedColor} + background={background} popup={getPopup()} popupContainsPt={popupContainsPt} // this should prohbably test to see if the click pt is actually within the picker selector list popup. /> @@ -145,7 +139,7 @@ export const ColorPicker = (props: IColorPickerProps) => { return formLabel ? (
-
+
{formLabel}
{colorPicker} diff --git a/packages/components/src/components/Dropdown/Dropdown.tsx b/packages/components/src/components/Dropdown/Dropdown.tsx index b9b6f01b8..783c7daa4 100644 --- a/packages/components/src/components/Dropdown/Dropdown.tsx +++ b/packages/components/src/components/Dropdown/Dropdown.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { FaCaretDown, FaCaretLeft, FaCaretRight, FaCaretUp } from 'react-icons/fa'; import { Popup, PopupTrigger } from '..'; -import { Colors, IGlobalProps, Placement, Type, getFontSize, getHeight, isDark, getFormLabelSize } from '../../global'; +import { Colors, IGlobalProps, Placement, Type, getFontSize, getHeight, getFormLabelSize } from '../../global'; import { IconButton } from '../IconButton'; import { ListBox } from '../ListBox'; import { IListItemProps, ListItem } from '../ListItem'; @@ -92,7 +92,7 @@ export const Dropdown = (props: IDropdownProps) => { textTransform: uppercase ? 'uppercase' : undefined, borderColor: getBorderColor(), background, - color: color && background ? color : type == Type.TERT ? (isDark(color) ? Colors.WHITE : Colors.BLACK) : color, + color: color, }; const backgroundProperties: React.CSSProperties = { @@ -116,9 +116,9 @@ export const Dropdown = (props: IDropdownProps) => {
{selectedVal && }
- +
-
+
); case DropdownType.CLICK: @@ -127,9 +127,9 @@ export const Dropdown = (props: IDropdownProps) => {
- +
-
+
); } @@ -157,6 +157,7 @@ export const Dropdown = (props: IDropdownProps) => { size={size} fillWidth={true} color={color} + background={background} popup={ { - const { - size, - height, - maxItems, - items, - dropdownSearchType, - selectedVal, - // setSelectedVal, - tooltip, - title = "DropdownSearch", - type, - width, - color - } = props + const { size, height, maxItems, items, dropdownSearchType, type, width, color } = props; - // const [selectedItem, setSelectedItem] = useState< - // IListItemProps | undefined - // >(selectedVal) + // const [selectedItem, setSelectedItem] = useState< + // IListItemProps | undefined + // >(selectedVal) - const [searchTerm, setSearchTerm] = useState(undefined) - const [isEditing, setIsEditing] = useState(false) - const [active, setActive] = useState(false) + const [searchTerm, setSearchTerm] = useState(undefined); + const [isEditing, setIsEditing] = useState(false); + const [active, setActive] = useState(false); - const getToggle = () => { - switch (dropdownSearchType) { - case DropdownSearchType.SELECT: - return (
{ - e.stopPropagation() - !isEditing && setIsEditing(true) - }} - > - {/* {selectedItem && !isEditing ? ( + const getToggle = () => { + switch (dropdownSearchType) { + case DropdownSearchType.SELECT: + return ( +
{ + e.stopPropagation(); + !isEditing && setIsEditing(true); + }}> + {/* {selectedItem && !isEditing ? ( ) : ( */} -
- { - // setSearchTerm(val) - // }} - size={Size.SMALL} - setEditing={setIsEditing} - /> -
- {/* )} */} -
- } - inactive - /> -
-
-
); - case DropdownSearchType.CLICK: - default: - return ( -
-
- ) - } - } +
+ { + // setSearchTerm(val) + // }} + size={Size.SMALL} + setEditing={setIsEditing} + /> +
+ {/* )} */} +
+ } inactive /> +
+
+
+ ); + case DropdownSearchType.CLICK: + default: + return
; + } + }; - return ( -
- - } - /> -
- ) -} + return ( +
+ + } + /> +
+ ); +}; diff --git a/packages/components/src/components/EditableText/EditableText.tsx b/packages/components/src/components/EditableText/EditableText.tsx index c361cf183..56cbc064d 100644 --- a/packages/components/src/components/EditableText/EditableText.tsx +++ b/packages/components/src/components/EditableText/EditableText.tsx @@ -1,21 +1,21 @@ -import React, { useState } from 'react' -import { Colors, IGlobalProps, Size, TextAlignment, Type, getFontSize, getFormLabelSize, getHeight, isDark } from '../../global' -import './EditableText.scss' -import { Toggle, ToggleType } from '../Toggle' -import { FaEye, FaEyeSlash} from 'react-icons/fa' +import React, { useState } from 'react'; +import { Colors, IGlobalProps, Size, TextAlignment, Type, getFontSize, getFormLabelSize, getHeight, isDark } from '../../global'; +import './EditableText.scss'; +import { Toggle, ToggleType } from '../Toggle'; +import { FaEye, FaEyeSlash } from 'react-icons/fa'; export interface IEditableTextProps extends IGlobalProps { - val?: string | number - setVal?: (newText: string | number) => unknown - onEnter?: (newText: string | number) => unknown - setEditing?: (bool: boolean) => unknown - placeholder?: string - editing?: boolean - size?: Size - height?: number - multiline?: boolean - textAlign?: TextAlignment - password?: boolean + val?: string | number; + setVal?: (newText: string | number) => unknown; + onEnter?: (newText: string | number) => unknown; + setEditing?: (bool: boolean) => unknown; + placeholder?: string; + editing?: boolean; + size?: Size; + height?: number; + multiline?: boolean; + textAlign?: TextAlignment; + password?: boolean; } /** @@ -25,152 +25,149 @@ export interface IEditableTextProps extends IGlobalProps { * @returns */ export const EditableText = (props: IEditableTextProps) => { - const [valLoc, setValLoc] = useState('') - const [editingLoc, setEditingLoc] = useState(false) - const { - height, - size, - val = valLoc, - setVal = setValLoc, - onEnter, - setEditing = setEditingLoc, - color = Colors.MEDIUM_BLUE, - background, - type = Type.PRIM, - placeholder, - width, - multiline, - textAlign = 'left', - formLabel, - formLabelPlacement, - fillWidth, - password, - editing = password ? true : editingLoc, - style - } = props - const [showPassword, setShowPassword] = useState(false) + const [valLoc, setValLoc] = useState(''); + const [editingLoc, setEditingLoc] = useState(false); + const { + height, + size, + val = valLoc, + setVal = setValLoc, + onEnter, + setEditing = setEditingLoc, + color = Colors.MEDIUM_BLUE, + background, + type = Type.PRIM, + placeholder, + width, + textAlign = 'left', + formLabel, + formLabelPlacement, + fillWidth, + password, + editing = password ? true : editingLoc, + style, + } = props; + const [showPassword, setShowPassword] = useState(false); - const handleOnChange = (event: React.ChangeEvent) => { - setVal(event.target.value) - } - const handleKeyPress = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - onEnter?.((event.target as HTMLInputElement).value) - } - } + const handleOnChange = (event: React.ChangeEvent) => { + setVal(event.target.value); + }; + const handleKeyPress = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + onEnter?.((event.target as HTMLInputElement).value); + } + }; - const getBorderColor = (): Colors | string | undefined => { - switch(type){ - case Type.PRIM: - return undefined; - case Type.SEC: - return color; - case Type.TERT: - if (editing) return color; - else return color; - } - } + const getBorderColor = (): Colors | string | undefined => { + switch (type) { + case Type.PRIM: + return undefined; + case Type.SEC: + return color; + case Type.TERT: + if (editing) return color; + else return color; + } + }; - const getColor = (): Colors | string | undefined => { - if (color && background) return color; - switch(type){ - case Type.PRIM: - return color; - case Type.SEC: - return color; - case Type.TERT: - if (isDark(color)) return Colors.WHITE; - else return Colors.BLACK - } - } + const getColor = (): Colors | string | undefined => { + if (color && background) return color; + switch (type) { + case Type.PRIM: + return color; + case Type.SEC: + return color; + case Type.TERT: + if (isDark(color)) return Colors.WHITE; + else return Colors.BLACK; + } + }; - const getBackground = (): Colors | string | undefined => { - if (background) return background; - switch(type){ - case Type.PRIM: - return color; - case Type.SEC: - return color; - case Type.TERT: - return color - } - } + const getBackground = (): Colors | string | undefined => { + if (background) return background; + switch (type) { + case Type.PRIM: + return color; + case Type.SEC: + return color; + case Type.TERT: + return color; + } + }; - const defaultProperties: React.CSSProperties = { - height: getHeight(height, size), - minHeight: getHeight(height, size), - width: fillWidth ? '100%' : width, - padding: undefined, - fontWeight: 500, - fontSize: getFontSize(size), - fontFamily: 'sans-serif', - borderColor: getBorderColor(), - color: getColor() - } + const defaultProperties: React.CSSProperties = { + height: getHeight(height, size), + minHeight: getHeight(height, size), + width: fillWidth ? '100%' : width, + padding: undefined, + fontWeight: 500, + fontSize: getFontSize(size), + fontFamily: 'sans-serif', + borderColor: getBorderColor(), + color: getColor(), + }; - const backgroundProperties: React.CSSProperties = { - background: getBackground() - } + const backgroundProperties: React.CSSProperties = { + background: getBackground(), + }; - const editableText: JSX.Element = ( -
setEditing(true)} - > - {editing ? ( - { - !password && setEditing(false) - }} - defaultValue={val} - > - ) : ( -
- {val ? val : placeholder} + const editableText: JSX.Element = ( +
setEditing(true)}> + {editing ? ( + { + !password && setEditing(false); + }} + defaultValue={val}> + ) : ( +
+ {val ? val : placeholder} +
+ )} + {password && ( +
+ setShowPassword(!showPassword)} + tooltip={`${showPassword ? 'Hide' : 'Show'} Password`} + icon={} + iconFalse={} + /> +
+ )} +
- )} - {password &&
- setShowPassword(!showPassword)} - tooltip={`${showPassword ? 'Hide' : 'Show'} Password`} - icon={} - iconFalse={} - /> -
} -
-
- ) + ); -return ( - formLabel ? -
-
{formLabel}
- {editableText} -
- : - editableText - ) -} + return formLabel ? ( +
+
+ {formLabel} +
+ {editableText} +
+ ) : ( + editableText + ); +}; diff --git a/packages/components/src/components/IconButton/IconButton.scss b/packages/components/src/components/IconButton/IconButton.scss index f899dc50f..3f0dd26ea 100644 --- a/packages/components/src/components/IconButton/IconButton.scss +++ b/packages/components/src/components/IconButton/IconButton.scss @@ -45,7 +45,7 @@ &.inactive { .background { - filter: opacity(0) !important; + filter: opacity(0); } } @@ -54,13 +54,13 @@ filter: opacity(0); &.active { - filter: opacity(0.2) !important; + filter: opacity(0.5); } } &:hover { .background { - filter: opacity(0.2); + filter: opacity(0.3); } } } @@ -70,7 +70,7 @@ filter: opacity(0); &.active { - filter: opacity(0.2) !important; + filter: opacity(0.2); } } @@ -85,10 +85,17 @@ &:hover { box-shadow: global.$standard-button-shadow; } + .background { + filter: opacity(0); + + &.active { + filter: opacity(0.5); + } + } &:hover { .background { - filter: brightness(0.8); + filter: opacity(0.3); } } } diff --git a/packages/components/src/components/IconButton/IconButton.tsx b/packages/components/src/components/IconButton/IconButton.tsx index 048048b20..f61f589b9 100644 --- a/packages/components/src/components/IconButton/IconButton.tsx +++ b/packages/components/src/components/IconButton/IconButton.tsx @@ -1,6 +1,6 @@ import { Tooltip } from '@mui/material'; import React from 'react'; -import { Colors, Size, Type, getFontSize, getHeight, isDark, getFormLabelSize } from '../../global'; +import { Colors, Size, Type, getFontSize, getHeight, getFormLabelSize } from '../../global'; import { IButtonProps } from '../Button'; import './IconButton.scss'; @@ -15,6 +15,7 @@ export const IconButton = (props: IButtonProps) => { type = Type.PRIM, color = Colors.MEDIUM_BLUE, background, + filter, label, height, size = Size.SMALL, @@ -71,6 +72,7 @@ export const IconButton = (props: IButtonProps) => { case Type.SEC: return color; case Type.TERT: + return ''; if (colorPicker) return colorPicker; if (active) return color; else return color; @@ -85,12 +87,7 @@ export const IconButton = (props: IButtonProps) => { case Type.SEC: return color; case Type.TERT: - if (colorPicker) { - if (isDark(colorPicker)) return Colors.WHITE; - else return Colors.BLACK; - } - if (isDark(color)) return Colors.WHITE; - else return Colors.BLACK; + return ''; } }; @@ -102,15 +99,14 @@ export const IconButton = (props: IButtonProps) => { case Type.SEC: return color; case Type.TERT: - if (colorPicker) return colorPicker; - else return color; + return ''; //color; } }; const defaultProperties: React.CSSProperties = { - height: getHeight(+(height ?? 0), size), - width: fillWidth ? '100%' : getHeight(+(height ?? 0), size), - minWidth: getHeight(+(height ?? 0), size), + height: getHeight(height, size), + width: fillWidth ? '100%' : getHeight(height, size), + minWidth: getHeight(height, size), fontWeight: 500, fontSize: getFontSize(size, true), borderColor: getBorderColor(), @@ -119,6 +115,7 @@ export const IconButton = (props: IButtonProps) => { const backgroundProperties: React.CSSProperties = { background: getBackground(), + filter, }; const iconButton: JSX.Element = ( @@ -126,9 +123,9 @@ export const IconButton = (props: IButtonProps) => {
{icon} - {colorPicker && type !== Type.TERT &&
} + {colorPicker &&
} {label && !hideLabel && ( -
+
{label}
)} @@ -140,7 +137,7 @@ export const IconButton = (props: IButtonProps) => { return formLabel ? (
-
+
{formLabel}
{iconButton} diff --git a/packages/components/src/components/ListItem/ListItem.tsx b/packages/components/src/components/ListItem/ListItem.tsx index e04c6fbee..6bb3e3824 100644 --- a/packages/components/src/components/ListItem/ListItem.tsx +++ b/packages/components/src/components/ListItem/ListItem.tsx @@ -85,7 +85,7 @@ export const ListItem = (props: IListItemProps) => {
diff --git a/packages/components/src/components/MultiToggle/MultiToggle.tsx b/packages/components/src/components/MultiToggle/MultiToggle.tsx index 7fff12c8e..0f659c5ca 100644 --- a/packages/components/src/components/MultiToggle/MultiToggle.tsx +++ b/packages/components/src/components/MultiToggle/MultiToggle.tsx @@ -1,29 +1,29 @@ -import * as React from 'react' -import { useState } from 'react' -import { Colors, IGlobalProps, Type } from '../../global' -import { Group } from '../Group' -import { IconButton } from '../IconButton' -import { Popup } from '../Popup' -import { IToggleProps, Toggle, ToggleType } from '../Toggle' +import * as React from 'react'; +import { useState } from 'react'; +import { Colors, IGlobalProps, Type } from '../../global'; +import { Group } from '../Group'; +import { IconButton } from '../IconButton'; +import { Popup } from '../Popup'; +import { IToggleProps, Toggle, ToggleType } from '../Toggle'; export interface IToggleItemProps extends IToggleProps { - val: string + val: string; } export interface IMultiToggleProps extends IGlobalProps { - items: IToggleItemProps[] + items: IToggleItemProps[]; multiSelect?: boolean; - defaultSelectedItems?: (string|number) | ((string|number)[]), - selectedItems?: (string | number) | ((string|number)[]), - onSelectionChange?: (val: (string|number) | (string|number)[], added: boolean) => unknown, + defaultSelectedItems?: (string | number) | (string | number)[]; + selectedItems?: (string | number) | (string | number)[]; + onSelectionChange?: (val: (string | number) | (string | number)[], added: boolean) => unknown; isToggle?: boolean; toggleStatus?: boolean; } -function promoteToArrayOrUndefined(d : (string|number)[]|(string|number)|undefined) { - return d instanceof Array || d === undefined ? d: [d]; +function promoteToArrayOrUndefined(d: (string | number)[] | (string | number) | undefined) { + return d instanceof Array || d === undefined ? d : [d]; } -function promoteToArray(d : (string|number)[]|(string|number)|undefined) { +function promoteToArray(d: (string | number)[] | (string | number) | undefined) { return promoteToArrayOrUndefined(d) ?? []; } @@ -32,56 +32,67 @@ export const MultiToggle = (props: IMultiToggleProps) => { const initVal = (!init ? undefined : promoteToArrayOrUndefined(props.defaultSelectedItems)) ?? promoteToArrayOrUndefined(props.selectedItems) ?? []; init = false; - const [selectedItemsLocal, setSelectedItemsLocal] = useState(initVal as (string|number) | ((string|number)[])); + const [selectedItemsLocal, setSelectedItemsLocal] = useState(initVal as (string | number) | (string | number)[]); const { items, selectedItems = selectedItemsLocal, tooltip, tooltipPlacement = 'top', onSelectionChange, color, background } = props; const itemsMap = new Map(); - items.forEach((item) => itemsMap.set(item.val, item)); - return
- - - {promoteToArray(selectedItems).length < 2 ? null : -
- + -
} -
} - isToggle={props.isToggle} - toggleFunc={() => { - const selItem = items.find(item => promoteToArray(selectedItems).includes(item.val)); - selItem && setSelectedItemsLocal([selItem.val]); - }} - type={props.type} - label={props.isToggle ? props.label : undefined} - toggleStatus={props.isToggle ? props.toggleStatus : undefined} - color={color} - popup={ - {items.map((item, i) => - { - const selected = new Set(); - promoteToArray(selectedItems).forEach(val => val && selected.add(val)); - const toAdd = !props.multiSelect || !selected.has(item.val) - if (!toAdd) selected.delete(item.val); - else item.val && selected.add(item.val); - onSelectionChange?.(item.val, toAdd); - setSelectedItemsLocal(props.multiSelect ? Array.from(selected) : item.val); - e.stopPropagation(); - }}/> - )} - } - /> -
-} \ No newline at end of file + items.forEach(item => itemsMap.set(item.val, item)); + return ( +
+ + + {promoteToArray(selectedItems).length < 2 ? null :
+
} +
+ ) + } + isToggle={props.isToggle} + toggleFunc={() => { + const selItem = items.find(item => promoteToArray(selectedItems).includes(item.val)); + selItem && setSelectedItemsLocal([selItem.val]); + }} + type={props.type} + label={props.isToggle ? props.label : undefined} + toggleStatus={props.isToggle ? props.toggleStatus : undefined} + color={color} + background={background} + popup={ + + {items.map((item, i) => ( + { + const selected = new Set(); + promoteToArray(selectedItems).forEach(val => val && selected.add(val)); + const toAdd = !props.multiSelect || !selected.has(item.val); + if (!toAdd) selected.delete(item.val); + else item.val && selected.add(item.val); + onSelectionChange?.(item.val, toAdd); + setSelectedItemsLocal(props.multiSelect ? Array.from(selected) : item.val); + e.stopPropagation(); + }} + /> + ))} + + } + /> +
+ ); +}; diff --git a/packages/components/src/components/NumberDropdown/NumberDropdown.tsx b/packages/components/src/components/NumberDropdown/NumberDropdown.tsx index 7f12198d5..b5f55bacf 100644 --- a/packages/components/src/components/NumberDropdown/NumberDropdown.tsx +++ b/packages/components/src/components/NumberDropdown/NumberDropdown.tsx @@ -20,11 +20,41 @@ export interface INumberDropdownProps extends INumberProps { export const NumberDropdown = (props: INumberDropdownProps) => { const [numberLoc, setNumberLoc] = useState(0); - const { fillWidth, numberDropdownType = false, color = Colors.MEDIUM_BLUE, type, formLabelPlacement, showPlusMinus, min, max, unit, step = 1, number = numberLoc, setNumber = setNumberLoc, size, formLabel, tooltip } = props; + const { + fillWidth, // + numberDropdownType = false, + color = Colors.MEDIUM_BLUE, + type, + formLabelPlacement, + showPlusMinus, + min, + max, + unit, + background, + step = 1, + number = numberLoc, + setNumber = setNumberLoc, + size, + formLabel, + tooltip, + } = props; const [isOpen, setOpen] = useState(false); let toggleText = number.toString(); if (unit) toggleText = toggleText + unit; - let toggle = setOpen(!isOpen)} />; + let toggle = ( + setOpen(!isOpen)} + /> + ); if (showPlusMinus) { toggle = ( @@ -75,23 +105,23 @@ export const NumberDropdown = (props: INumberDropdownProps) => { break; case 'slider': default: - popup = ; + popup = ; break; case 'input': - popup = ; + popup = ; break; } const numberDropdown: JSX.Element = ( -
- +
+
); return formLabel ? ( -
+
{numberDropdown} -
+
setOpen(!isOpen)} style={{ cursor: 'pointer', height: '25%', fontSize: getFormLabelSize(size) }}> {formLabel}
diff --git a/packages/components/src/components/NumberInput/NumberInput.tsx b/packages/components/src/components/NumberInput/NumberInput.tsx index 33573d000..56c61a41e 100644 --- a/packages/components/src/components/NumberInput/NumberInput.tsx +++ b/packages/components/src/components/NumberInput/NumberInput.tsx @@ -1,89 +1,76 @@ -import * as React from 'react' -import { Colors, INumberProps , Type, getFormLabelSize, getHeight } from '../../global' -import './NumberInput.scss' -import { useState } from 'react' -import { Group } from '../Group' -import { Toggle, ToggleType } from '../Toggle' -import { IconButton } from '../IconButton' -import * as fa from 'react-icons/fa' -import { EditableText } from '../EditableText' +import * as React from 'react'; +import { Colors, INumberProps, getFormLabelSize, getHeight } from '../../global'; +import './NumberInput.scss'; +import { useState } from 'react'; +import { Group } from '../Group'; +import { IconButton } from '../IconButton'; +import * as fa from 'react-icons/fa'; +import { EditableText } from '../EditableText'; export interface INumberInputProps extends INumberProps { - showPlusMinus?: boolean + showPlusMinus?: boolean; } export const NumberInput = (props: INumberInputProps) => { - const [numberLoc, setNumberLoc] = useState(10) - const { - color = Colors.MEDIUM_BLUE, - type, - formLabelPlacement, - showPlusMinus, - min, - max, - unit = '', - width, - fillWidth = width ? true : false, - step = 1, - number = numberLoc, - setNumber = setNumberLoc, - size, - formLabel, - tooltip } = - props; + const [numberLoc, setNumberLoc] = useState(10); + const { color = Colors.MEDIUM_BLUE, type, formLabelPlacement, showPlusMinus, min, max, unit = '', width, fillWidth = width ? true : false, step = 1, number = numberLoc, setNumber = setNumberLoc, size, formLabel } = props; + + let input = ( + setNumber(!isNaN(Number(val)) ? Number(val) : number)} + /> + ); - let input = setNumber(!isNaN(Number(val)) ? Number(val) : number)} - />; - if (showPlusMinus) { - input = - {input} - } - color={color} - onClick={(e) => { - e.stopPropagation(); - setNumber(number - step); - }} - inactive={number - step < min} - tooltip={`Subtract ${step}${unit}`} - /> - } - color={color} - onClick={(e) => { - e.stopPropagation(); - setNumber(number + step); - }} - inactive={number + step > max} - tooltip={`Add ${step}${unit}`} - /> - + input = ( + + {input} + } + color={color} + onClick={e => { + e.stopPropagation(); + setNumber(number - step); + }} + inactive={number - step < min} + tooltip={`Subtract ${step}${unit}`} + /> + } + color={color} + onClick={e => { + e.stopPropagation(); + setNumber(number + step); + }} + inactive={number + step > max} + tooltip={`Add ${step}${unit}`} + /> + + ); } - - return ( - formLabel ? -
-
{formLabel}
-
+ return formLabel ? ( +
+
+ {formLabel} +
+
{input}
- : -
+ ) : ( +
{input}
- ) -} \ No newline at end of file + ); +}; diff --git a/packages/components/src/components/Popup/Popup.tsx b/packages/components/src/components/Popup/Popup.tsx index 5a58fee29..82e60f343 100644 --- a/packages/components/src/components/Popup/Popup.tsx +++ b/packages/components/src/components/Popup/Popup.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; -import { Colors, IGlobalProps, Placement, Size, isDark } from '../../global'; +import { IGlobalProps, Placement, Size } from '../../global'; import { Toggle, ToggleType } from '../Toggle'; import './Popup.scss'; import { Popper } from '@mui/material'; @@ -57,7 +57,7 @@ export const Popup = (props: IPopupProps) => { height, fillWidth, iconPlacement = 'left', - background = isDark(color) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY, + background, popupContainsPt, } = props; @@ -116,7 +116,7 @@ export const Popup = (props: IPopupProps) => { size={size} type={type} color={color} - background={props.isToggle ? undefined : background} + background={background} toggleType={ToggleType.BUTTON} icon={icon} iconPlacement={iconPlacement} @@ -138,7 +138,7 @@ export const Popup = (props: IPopupProps) => {
{ const toDecimal = (num: number) => (decimals !== undefined ? Math.round(num * Math.pow(10, decimals)) / Math.pow(10, decimals) : num); const getLeftPos = (locVal: number) => { - const dragger = getHeight(+(height || 0), size); + const dragger = +getHeight(height, size); return ((locVal - min) / (max - min)) * (width - dragger); }; @@ -65,8 +65,8 @@ export const Slider = (props: ISliderProps) => { background: color, color: isDark(color) ? Colors.LIGHT_GRAY : Colors.DARK_GRAY, fontSize: getFontSize(size), - height: getHeight(+(height || 0), size), - width: getHeight(+(height || 0), size), + height: getHeight(height, size), + width: getHeight(height, size), top: 0, }}> {toDecimal(locVal)} @@ -132,7 +132,7 @@ export const Slider = (props: ISliderProps) => { lastEndVal = endNumberLoc; }} style={{ - padding: `5px 0px ${getHeight(+(height || 0), size)}px 0px`, + padding: `5px 0px ${getHeight(height, size)}px 0px`, width: fillWidth ? '100%' : 'fit-content', }}>
{ r && new ResizeObserver(() => setWidth(+(r?.clientWidth ?? 100))).observe(r); setWidth(+(r?.clientWidth ?? 100)); }} - style={{ height: getHeight(+(height || 0), size) }} + style={{ height: getHeight(height, size) }} onPointerDown={onPointerDown}> {ValSlider}
selected, false -> unselected - toggleType?: ToggleType - iconFalse?: JSX.Element | string + toggleStatus?: boolean; // true -> selected, false -> unselected + toggleType?: ToggleType; + iconFalse?: JSX.Element | string; + triState?: boolean; } export const Toggle = (props: IToggleProps) => { - const [toggleStatusLoc, setToggleStatusLoc] = useState(true); - const { - toggleStatus = toggleStatusLoc, - toggleType = ToggleType.CHECKBOX, - type = Type.SEC, - style, - color, - background, - text, - icon, - iconFalse = icon, - height, - inactive, - label, - iconPlacement, - onPointerDown, - onClick, - tooltip, - tooltipPlacement = 'top', - size = Size.SMALL, - formLabel, - formLabelPlacement, - fillWidth, - align - } = props + const [toggleStatusLoc, setToggleStatusLoc] = useState(true); + const { + toggleStatus = toggleStatusLoc, + toggleType = ToggleType.CHECKBOX, + type = Type.SEC, + color, + background, + text, + icon, + iconFalse = icon, + height, + inactive, + label, + iconPlacement, + onPointerDown, + onClick, + triState, + tooltip, + tooltipPlacement = 'top', + size = Size.SMALL, + formLabel, + formLabelPlacement, + fillWidth, + align, + } = props; - /** - * Pointer down - * @param e - */ - const handlePointerDown = (e: React.PointerEvent) => { - if (!inactive && onPointerDown){ - e.stopPropagation(); - e.preventDefault(); - onPointerDown(e) - } - } + /** + * Pointer down + * @param e + */ + const handlePointerDown = (e: React.PointerEvent) => { + if (!inactive && onPointerDown) { + e.stopPropagation(); + e.preventDefault(); + onPointerDown(e); + } + }; - /** - * Single click - * @param e - */ - const handleClick = (e: React.MouseEvent) => { - if (toggleStatus === toggleStatusLoc) { - setToggleStatusLoc(!toggleStatus) - } - - if (!inactive && onClick) { - e.stopPropagation(); - e.preventDefault(); - onClick(e); - } - } + /** + * Single click + * @param e + */ + const handleClick = (e: React.MouseEvent) => { + if (toggleStatus === toggleStatusLoc) { + setToggleStatusLoc(!toggleStatus); + } - const defaultProperties = { - height: getHeight(height, size), - borderColor: color - } + if (!inactive && onClick) { + e.stopPropagation(); + e.preventDefault(); + onClick(e); + } + }; - let toggleElement: JSX.Element; + const defaultProperties = { + height: getHeight(height, size), + borderColor: color, + }; - switch(toggleType) { - case ToggleType.BUTTON: - toggleElement = ( - + Click to add/remove the {tag} tag
}> + {this.getButtonIcon(this.View, key)} )); // prettier-ignore - return !buttons.length ? null : ( + const audioannos = StrListCast(this.View.Document[Doc.LayoutFieldKey(this.View.Document) + '_audioAnnotations_text']); + return !buttons.length && !audioannos.length ? null : (
+ {audioannos.length ? this.renderAudioButtons(this.View, audioannos.lastElement()) : null} {buttons}
); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 51b9a4333..6955e5401 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -17,7 +17,7 @@ import { BsMarkdownFill } from 'react-icons/bs'; import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; -import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; import { Id, ToString } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; @@ -246,7 +246,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent void = emptyFunction; target.$mediaState = mediaState.Recording; - DictationManager.recordAudioAnnotation(target[DocData], Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore + DictationManager.recordAudioAnnotation(target, Doc.LayoutFieldKey(target), stop => { stopFunc = stop }); // prettier-ignore const reactionDisposer = reaction( () => target.mediaState, -- cgit v1.2.3-70-g09d2 From a3d6fae1482c61ca725d0a103f13b621aa32b3e3 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 5 Apr 2025 14:29:03 -0400 Subject: fixed multitoggle behavior to only toggle submenu closed. switched to hard-light for masking ink. fixed to make menu toggle take effect immediately. --- packages/components/src/components/Group/Group.tsx | 68 +++++------ .../components/MultiToggle/MultiToggle.stories.tsx | 125 ++++++++++----------- .../src/components/MultiToggle/MultiToggle.tsx | 36 +++--- packages/components/src/components/Popup/Popup.tsx | 22 ++-- src/client/documents/Documents.ts | 37 +++--- src/client/views/collections/CollectionSubView.tsx | 7 +- .../views/collections/FlashcardPracticeUI.tsx | 5 +- .../collectionFreeForm/CollectionFreeFormView.scss | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 1 - 10 files changed, 139 insertions(+), 169 deletions(-) (limited to 'packages/components/src') diff --git a/packages/components/src/components/Group/Group.tsx b/packages/components/src/components/Group/Group.tsx index 7abe4a1c7..6275a09dd 100644 --- a/packages/components/src/components/Group/Group.tsx +++ b/packages/components/src/components/Group/Group.tsx @@ -1,49 +1,33 @@ -import React from 'react' -import './Group.scss' -import { Colors, IGlobalProps, getFontSize, isDark , getFormLabelSize } from '../../global'; +import React from 'react'; +import './Group.scss'; +import { Colors, IGlobalProps, getFontSize, isDark, getFormLabelSize } from '../../global'; export interface IGroupProps extends IGlobalProps { - children: any - rowGap?: number; - columnGap?: number; - padding?: number | string; + children: any; + rowGap?: number; + columnGap?: number; + padding?: number | string; } export const Group = (props: IGroupProps) => { - const { - children, - width = '100%', - rowGap = 5, - columnGap = 5, - padding = 0, - formLabel, - formLabelPlacement, - size, - style, - color, - fillWidth - } = props + const { children, width = '100%', rowGap = 5, columnGap = 5, padding = 0, formLabel, formLabelPlacement, size, style, fillWidth } = props; - const group: JSX.Element = - ( -
-
{children}
-
- ) + const group: JSX.Element = ( +
+
+ {children} +
+
+ ); - return ( - formLabel ? -
-
{formLabel}
- {group} -
- : - group - ) -} + return formLabel ? ( +
+
+ {formLabel} +
+ {group} +
+ ) : ( + group + ); +}; diff --git a/packages/components/src/components/MultiToggle/MultiToggle.stories.tsx b/packages/components/src/components/MultiToggle/MultiToggle.stories.tsx index e71423d7a..b9ba45f72 100644 --- a/packages/components/src/components/MultiToggle/MultiToggle.stories.tsx +++ b/packages/components/src/components/MultiToggle/MultiToggle.stories.tsx @@ -1,69 +1,68 @@ -import { Meta, Story } from '@storybook/react' -import React from 'react' -import { IMultiToggleProps, MultiToggle } from './MultiToggle' -import { FaAlignLeft, FaAlignCenter, FaAlignJustify, FaAlignRight } from 'react-icons/fa' +import { Meta, Story } from '@storybook/react'; +import React from 'react'; +import { IMultiToggleProps, MultiToggle } from './MultiToggle'; +import { FaAlignLeft, FaAlignCenter, FaAlignJustify, FaAlignRight } from 'react-icons/fa'; export default { - title: 'Dash/MultiToggle', - component: MultiToggle, - argTypes: {}, -} as Meta + title: 'Dash/MultiToggle', + component: MultiToggle, + argTypes: {}, +} as Meta; -const MultiToggleStory: Story = (args) => -export const MultiToggleOne = MultiToggleStory.bind({}) +const MultiToggleStory: Story = args => ; +export const MultiToggleOne = MultiToggleStory.bind({}); MultiToggleOne.args = { - tooltip: "Text alignment", - label: "Alignment", - defaultSelectedItems: "center", - toggleStatus: true, - isToggle: false, - items: [ - { - icon: , - tooltip: 'Align left', - val: "left" - }, - { - icon: , - tooltip: 'Align center', - val: "center" - }, - { - icon: , - tooltip: 'Align right', - val: "right" - }, - { - icon: , - tooltip: 'Justify', - val: "justify" - }, - ] -} + tooltip: 'Text alignment', + label: 'Alignment', + defaultSelectedItems: 'center', + toggleStatus: true, + items: [ + { + icon: , + tooltip: 'Align left', + val: 'left', + }, + { + icon: , + tooltip: 'Align center', + val: 'center', + }, + { + icon: , + tooltip: 'Align right', + val: 'right', + }, + { + icon: , + tooltip: 'Justify', + val: 'justify', + }, + ], +}; -export const MultiToggleTwo = MultiToggleStory.bind({}) +export const MultiToggleTwo = MultiToggleStory.bind({}); MultiToggleTwo.args = { - tooltip: "Text Tags", - label: "Tags", - defaultSelectedItems : ["left"], - background: "green", - color: 'white', - multiSelect: true, - items: [ - { - icon: , - tooltip: 'Like', - val: "left" - }, - { - icon: , - tooltip: 'Todo', - val: "center" - }, - { - icon: , - tooltip: 'Idea', - val: "right" - }, - ] -} + tooltip: 'Text Tags', + label: 'Tags', + defaultSelectedItems: ['left'], + background: 'green', + color: 'white', + multiSelect: true, + items: [ + { + icon: , + tooltip: 'Like', + val: 'left', + }, + { + icon: , + tooltip: 'Todo', + val: 'center', + }, + { + icon: , + tooltip: 'Idea', + val: 'right', + }, + ], +}; diff --git a/packages/components/src/components/MultiToggle/MultiToggle.tsx b/packages/components/src/components/MultiToggle/MultiToggle.tsx index 0f659c5ca..0a6fb81c9 100644 --- a/packages/components/src/components/MultiToggle/MultiToggle.tsx +++ b/packages/components/src/components/MultiToggle/MultiToggle.tsx @@ -16,7 +16,6 @@ export interface IMultiToggleProps extends IGlobalProps { defaultSelectedItems?: (string | number) | (string | number)[]; selectedItems?: (string | number) | (string | number)[]; onSelectionChange?: (val: (string | number) | (string | number)[], added: boolean) => unknown; - isToggle?: boolean; toggleStatus?: boolean; } @@ -37,33 +36,30 @@ export const MultiToggle = (props: IMultiToggleProps) => { const itemsMap = new Map(); items.forEach(item => itemsMap.set(item.val, item)); return ( -
+
- - {promoteToArray(selectedItems).length < 2 ? null :
+
} -
- ) +
+ + {promoteToArray(selectedItems).length < 2 ? null :
+
} +
} - isToggle={props.isToggle} + isToggle={true} toggleFunc={() => { const selItem = items.find(item => promoteToArray(selectedItems).includes(item.val)); selItem && setSelectedItemsLocal([selItem.val]); }} type={props.type} - label={props.isToggle ? props.label : undefined} - toggleStatus={props.isToggle ? props.toggleStatus : undefined} + label={undefined} color={color} background={background} popup={ diff --git a/packages/components/src/components/Popup/Popup.tsx b/packages/components/src/components/Popup/Popup.tsx index 82e60f343..9e72ece87 100644 --- a/packages/components/src/components/Popup/Popup.tsx +++ b/packages/components/src/components/Popup/Popup.tsx @@ -20,11 +20,10 @@ export interface IPopupProps extends IGlobalProps { toggle?: JSX.Element; popup: JSX.Element | string | (() => JSX.Element); trigger?: PopupTrigger; - toggleStatus?: boolean; isOpen?: boolean; setOpen?: (b: boolean) => void; background?: string; - isToggle?: boolean; + isToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it. toggleFunc?: () => void; popupContainsPt?: (x: number, y: number) => boolean; } @@ -71,6 +70,7 @@ export const Popup = (props: IPopupProps) => { const rect = popperRef.current?.getBoundingClientRect(); const rect2 = toggleRef.current?.getBoundingClientRect(); if ( + !props.isToggle && (!rect2 || !(rect2.left < e.clientX && rect2.top < e.clientY && rect2.right > e.clientX && rect2.bottom > e.clientY)) && rect && !(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) && @@ -109,8 +109,8 @@ export const Popup = (props: IPopupProps) => { timeout = setTimeout(() => setOpen(false), 1000); } }}> - {toggle ?? ( -
+
+ {toggle ?? ( { iconPlacement={iconPlacement} text={text} label={props.label} - toggleStatus={isOpen || props.toggleStatus} + toggleStatus={isOpen} onClick={() => { if (trigger === PopupTrigger.CLICK) { - if (!props.isToggle || props.toggleStatus) { - setOpen(!isOpen); - } + setOpen(!isOpen); props.toggleFunc?.(); } }} fillWidth={fillWidth} /> -
- )} + )} +
{ ref={popperRef} style={{ width, height, background }} tabIndex={-1} - onPointerDown={e => { - e.stopPropagation(); - }} + onPointerDown={e => e.stopPropagation()} onPointerEnter={() => { if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { clearTimeout(timeout); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8458699fa..f1655e3cf 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -4,7 +4,6 @@ import { basename } from 'path'; import { ClientUtils, OmitKeys } from '../../ClientUtils'; import { DateField } from '../../fields/DateField'; import { CreateLinkToActiveAudio, Doc, FieldType, Opt, updateCachedAcls } from '../../fields/Doc'; -import { Initializing } from '../../fields/DocSymbols'; import { Id } from '../../fields/FieldSymbols'; import { HtmlField } from '../../fields/HtmlField'; import { InkField } from '../../fields/InkField'; @@ -983,26 +982,22 @@ export namespace Docs { export function InkDocument(points: PointData[], options: DocumentOptions = {}, strokeWidth: number, color: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, isInkMask: boolean) { const ink = InstanceFromProto(Prototypes.get(DocumentType.INK), '', { title: 'ink', ...options }); - const I = Doc.GetProto(ink); - // I.layout_hideOpenButton = true; // don't show open full screen button when selected - I.color = color; - I.fillColor = fillColor && fillColor !== 'transparent' ? fillColor : undefined; - I.stroke = new InkField(points); - I.stroke_width = strokeWidth; - I.stroke_bezier = strokeBezier; - I.stroke_startMarker = arrowStart; - I.stroke_endMarker = arrowEnd; - I.stroke_dash = dash; - I.stroke_isInkMask = isInkMask; - I.text_align = 'center'; - I.rotation = 0; - I.width_min = 1; - I.height_min = 1; - I.defaultDoubleClick = 'ignore'; - I.author_date = new DateField(); - I.acl_Guest = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; - // I.acl_Override = SharingPermissions.Unset; - I[Initializing] = false; + ink.$color = color; + ink.$fillColor = fillColor && fillColor !== 'transparent' ? fillColor : undefined; + ink.$stroke = new InkField(points); + ink.$stroke_width = strokeWidth; + ink.$stroke_bezier = strokeBezier; + ink.$stroke_startMarker = arrowStart; + ink.$stroke_endMarker = arrowEnd; + ink.$stroke_dash = dash; + ink.$stroke_isInkMask = isInkMask; + ink.$text_align = 'center'; + ink.$rotation = 0; + ink.$width_min = 1; + ink.$height_min = 1; + ink.$defaultDoubleClick = 'ignore'; + ink.$author_date = new DateField(); + ink.$acl_Guest = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.View; return ink; } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index cfd52f9ee..a88707c6f 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -13,6 +13,7 @@ import { BoolCast, Cast, DateCast, NumCast, ScriptCast, StrCast, toList } from ' import { WebField } from '../../../fields/URLField'; import { GetEffectiveAcl, TraceMobx } from '../../../fields/util'; import { GestureUtils } from '../../../pen-gestures/GestureUtils'; +import { Upload } from '../../../server/SharedMediaTypes'; import { DocServer } from '../../DocServer'; import { Networking } from '../../Network'; import { DocUtils } from '../../documents/DocUtils'; @@ -24,11 +25,11 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { SnappingManager } from '../../util/SnappingManager'; import { UndoManager } from '../../util/UndoManager'; import { ViewBoxBaseComponent } from '../DocComponent'; +import { DocumentViewProps } from '../nodes/DocumentContentsView'; +import { DocumentView } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; -import { FlashcardPracticeUI } from './FlashcardPracticeUI'; import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere'; -import { Upload } from '../../../server/SharedMediaTypes'; +import { FlashcardPracticeUI } from './FlashcardPracticeUI'; export enum docSortings { Time = 'time', diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx index 3bcdd843e..8cd9c5452 100644 --- a/src/client/views/collections/FlashcardPracticeUI.tsx +++ b/src/client/views/collections/FlashcardPracticeUI.tsx @@ -12,10 +12,11 @@ import { BoolCast, NumCast, StrCast } from '../../../fields/Types'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocumentView } from '../nodes/DocumentView'; import './FlashcardPracticeUI.scss'; import { StyleProp } from '../StyleProp'; import { FieldViewProps } from '../nodes/FieldView'; +import { DocumentViewProps } from '../nodes/DocumentContentsView'; export enum practiceMode { PRACTICE = 'practice', @@ -142,7 +143,6 @@ export class FlashcardPracticeUI extends ObservableReactComponent() { background={background} multiSelect={true} onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: this.Document, value: undefined, _readOnly_: false }))} - isToggle={false} toggleStatus={toggleStatus} label={selectedItems.length === 1 ? selectedItems[0] : this.label} items={items.map(item => ({ -- cgit v1.2.3-70-g09d2 From 40b8e5c30e7251db6258cac5b4ed5fb4a6a2418d Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 7 Apr 2025 15:20:36 -0400 Subject: added multiToggle option to allow popup to stay up until explicitly untoggled. fixed multitoggle to honor toggleStatus. fixed popup to follow target. fixed setting text highlight background fro popup menu. fixed flashcardui buttons to have background sized properly. --- package-lock.json | 24 ++++++++++++ package.json | 1 + .../src/components/MultiToggle/MultiToggle.tsx | 16 +++++++- packages/components/src/components/Popup/Popup.tsx | 43 +++++++++++++++------- src/client/util/CurrentUserUtils.ts | 5 ++- .../views/collections/CollectionCardDeckView.tsx | 3 +- .../views/collections/FlashcardPracticeUI.tsx | 7 ++-- src/client/views/nodes/FontIconBox/FontIconBox.tsx | 1 + .../views/nodes/formattedText/FormattedTextBox.tsx | 2 +- .../views/nodes/formattedText/RichTextMenu.tsx | 3 +- 10 files changed, 81 insertions(+), 24 deletions(-) (limited to 'packages/components/src') diff --git a/package-lock.json b/package-lock.json index 3b11eda7c..9acca90ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "@pinecone-database/pinecone": "^2.2.2", "@react-google-maps/api": "^2.19.2", "@react-spring/web": "^9.7.5", + "@thednp/position-observer": "^1.0.7", "@turf/turf": "^7.1.0", "@types/bezier-js": "^4.1.3", "@types/brotli": "^1.3.4", @@ -12081,6 +12082,29 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@thednp/position-observer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@thednp/position-observer/-/position-observer-1.0.7.tgz", + "integrity": "sha512-MkUAMMgqZPxy71hpcrKr9ZtedMk+oIFbFs5B8uKD857iuYKRJxgJtC1Itus14EEM4qMyeN0x47AUZJmZJQyXbQ==", + "license": "MIT", + "dependencies": { + "@thednp/shorty": "^2.0.10" + }, + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, + "node_modules/@thednp/shorty": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.10.tgz", + "integrity": "sha512-H+hs1lw3Yc1NfwG0b7F7YmVjxQZ31NO2+6zx+I+9XabHxdwPKjvYJnkKKXr7bSItgm2AFrfOn5+3veB6W4iauw==", + "license": "MIT", + "engines": { + "node": ">=16", + "pnpm": ">=8.6.0" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", diff --git a/package.json b/package.json index fe76189bf..1038c470e 100644 --- a/package.json +++ b/package.json @@ -123,6 +123,7 @@ "@pinecone-database/pinecone": "^2.2.2", "@react-google-maps/api": "^2.19.2", "@react-spring/web": "^9.7.5", + "@thednp/position-observer": "^1.0.7", "@turf/turf": "^7.1.0", "@types/bezier-js": "^4.1.3", "@types/brotli": "^1.3.4", diff --git a/packages/components/src/components/MultiToggle/MultiToggle.tsx b/packages/components/src/components/MultiToggle/MultiToggle.tsx index 0a6fb81c9..c1d610c34 100644 --- a/packages/components/src/components/MultiToggle/MultiToggle.tsx +++ b/packages/components/src/components/MultiToggle/MultiToggle.tsx @@ -17,6 +17,7 @@ export interface IMultiToggleProps extends IGlobalProps { selectedItems?: (string | number) | (string | number)[]; onSelectionChange?: (val: (string | number) | (string | number)[], added: boolean) => unknown; toggleStatus?: boolean; + showUntilToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it. } function promoteToArrayOrUndefined(d: (string | number)[] | (string | number) | undefined) { @@ -32,12 +33,23 @@ export const MultiToggle = (props: IMultiToggleProps) => { init = false; const [selectedItemsLocal, setSelectedItemsLocal] = useState(initVal as (string | number) | (string | number)[]); - const { items, selectedItems = selectedItemsLocal, tooltip, tooltipPlacement = 'top', onSelectionChange, color, background } = props; + const { + items, // + selectedItems = selectedItemsLocal, + tooltip, + toggleStatus, + tooltipPlacement = 'top', + onSelectionChange, + color, + background, + } = props; const itemsMap = new Map(); items.forEach(item => itemsMap.set(item.val, item)); return (
{ {promoteToArray(selectedItems).length < 2 ? null :
+
}
} - isToggle={true} + showUntilToggle={props.showUntilToggle ?? true} toggleFunc={() => { const selItem = items.find(item => promoteToArray(selectedItems).includes(item.val)); selItem && setSelectedItemsLocal([selItem.val]); diff --git a/packages/components/src/components/Popup/Popup.tsx b/packages/components/src/components/Popup/Popup.tsx index 9e72ece87..9e91a75cf 100644 --- a/packages/components/src/components/Popup/Popup.tsx +++ b/packages/components/src/components/Popup/Popup.tsx @@ -3,6 +3,7 @@ import { IGlobalProps, Placement, Size } from '../../global'; import { Toggle, ToggleType } from '../Toggle'; import './Popup.scss'; import { Popper } from '@mui/material'; +import PositionObserver from '@thednp/position-observer'; export enum PopupTrigger { CLICK = 'click', @@ -23,9 +24,10 @@ export interface IPopupProps extends IGlobalProps { isOpen?: boolean; setOpen?: (b: boolean) => void; background?: string; - isToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it. + showUntilToggle?: boolean; // whether popup stays open when background is clicked. muyst click toggle button tp close it. toggleFunc?: () => void; popupContainsPt?: (x: number, y: number) => boolean; + multitoggle?: boolean; } /** @@ -57,20 +59,21 @@ export const Popup = (props: IPopupProps) => { fillWidth, iconPlacement = 'left', background, + multitoggle, popupContainsPt, } = props; const triggerRef = useRef(null); const popperRef = useRef(null); - const toggleRef = useRef(null); + const [toggleRef, setToggleRef] = useState(null); let timeout = setTimeout(() => {}); const handlePointerAwayDown = (e: PointerEvent) => { const rect = popperRef.current?.getBoundingClientRect(); - const rect2 = toggleRef.current?.getBoundingClientRect(); + const rect2 = toggleRef?.getBoundingClientRect(); if ( - !props.isToggle && + !props.showUntilToggle && (!rect2 || !(rect2.left < e.clientX && rect2.top < e.clientY && rect2.right > e.clientX && rect2.bottom > e.clientY)) && rect && !(rect.left < e.clientX && rect.top < e.clientY && rect.right > e.clientX && rect.bottom > e.clientY) && @@ -82,22 +85,36 @@ export const Popup = (props: IPopupProps) => { } }; + let observer: PositionObserver | undefined = undefined; + const [previousPosition, setPreviousPosition] = useState(toggleRef?.getBoundingClientRect()); + useEffect(() => { if (isOpen) { window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); window.addEventListener('pointerdown', handlePointerAwayDown, { capture: true }); - return () => window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); - } - }, [isOpen, popupContainsPt]); - + if (toggleRef && multitoggle) { + (observer = new PositionObserver(entries => { + entries.forEach(entry => { + const currentPosition = entry.boundingClientRect; + if (Math.floor(currentPosition.top) !== Math.floor(previousPosition?.top ?? 0) || Math.floor(currentPosition.left) !== Math.floor(previousPosition?.left ?? 0)) { + // Perform actions when position changes + setPreviousPosition(currentPosition); // Update previous position + } + }); + })).observe(toggleRef); + } + return () => { + window.removeEventListener('pointerdown', handlePointerAwayDown, { capture: true }); + observer?.disconnect(); + }; + } else observer?.disconnect(); + }, [isOpen, toggleRef, popupContainsPt]); return (
{ - if (trigger === PopupTrigger.CLICK) setOpen(!isOpen); - }} + className={`trigger-container ${fillWidth && 'fillWidth'}`} + onClick={() => trigger === PopupTrigger.CLICK && setOpen(!isOpen)} onPointerEnter={() => { if (trigger === PopupTrigger.HOVER || trigger === PopupTrigger.HOVER_DELAY) { clearTimeout(timeout); @@ -109,7 +126,7 @@ export const Popup = (props: IPopupProps) => { timeout = setTimeout(() => setOpen(false), 1000); } }}> -
+
setToggleRef(R)}> {toggle ?? ( ; ignoreClick?: boolean; + showUntilToggle?: boolean; // whether the popup should stay open when the background is clicked. buttonText?: string; backgroundColor?: string; waitForDoubleClickToClick?: boolean; @@ -778,7 +779,7 @@ pie title Minerals in my tap water { title: "Circle", toolTip: "Circle (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "circle", toolType: Gestures.Circle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, { title: "Square", toolTip: "Square (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "square", toolType: Gestures.Rectangle, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, { title: "Line", toolTip: "Line (double tap to lock mode)", btnType: ButtonType.ToggleButton, icon: "minus", toolType: Gestures.Line, scripts: {onClick:`{ return setActiveTool(this.toolType, false, _readOnly_);}`, onDoubleClick:`{ return setActiveTool(this.toolType, true, _readOnly_);}`} }, - { title: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, + { title: "Ink", toolTip: "Ink", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Ink, showUntilToggle: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }, subMenu: [ { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", toolType: InkInkTool.Pen, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }}, { title: "Highlight",toolTip: "Highlight (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", toolType: InkInkTool.Highlight, ignoreClick: true, scripts: {onClick:'{ return setActiveTool(this.toolType, true, _readOnly_);}' }}, @@ -786,7 +787,7 @@ pie title Minerals in my tap water ]}, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberSliderButton, toolType: InkProperty.StrokeWidth,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}, numBtnMin: 1, linearBtnWidth:40}, { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", toolType: InkProperty.StrokeColor,ignoreClick: true, scripts: {script: '{ return setInkProperty(this.toolType, value, _readOnly_);}'}, funcs: {hidden:"!activeInkTool()"}}, - { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, + { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.MultiToggleButton, toolType: InkTool.Eraser, showUntilToggle: true, scripts: {onClick:'{ return setActiveTool(this.toolType, false, _readOnly_);}' }, subMenu: [ { title: "Stroke", toolTip: "Eraser complete strokes",btnType: ButtonType.ToggleButton, icon: "eraser", toolType:InkEraserTool.Stroke, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, { title: "Segment", toolTip: "Erase between intersections",btnType:ButtonType.ToggleButton,icon:"xmark", toolType:InkEraserTool.Segment, ignoreClick: true, scripts: {onClick: '{ return setActiveTool(this.toolType, false, _readOnly_);}'}}, diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 756b37f99..50de7c601 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -23,7 +23,8 @@ import { undoable, UndoManager } from '../../util/UndoManager'; import { PinDocView, PinProps } from '../PinFuncs'; import { StyleProp } from '../StyleProp'; import { TagItem } from '../TagsView'; -import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocumentViewProps } from '../nodes/DocumentContentsView'; +import { DocumentView } from '../nodes/DocumentView'; import { FocusViewOptions } from '../nodes/FocusViewOptions'; import './CollectionCardDeckView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx index 8cd9c5452..f24a8acb7 100644 --- a/src/client/views/collections/FlashcardPracticeUI.tsx +++ b/src/client/views/collections/FlashcardPracticeUI.tsx @@ -63,8 +63,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent doc.title === 'Filter'); } // prettier-ignore @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore - btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale); - btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height)); + btnHeight = () => NumCast(this.filterDoc?.height); + btnWidth = () => (!this.filterDoc ? 1 : NumCast(this.filterDoc._width)); /** * Sets the practice mode answer style for flashcards @@ -135,7 +135,6 @@ export class FlashcardPracticeUI extends ObservableReactComponent= 1 ? undefined : `translateY(${this.btnHeight() / this._props.ScreenToLocalBoxXf().Scale - this.btnHeight()}px)`, }}> +
{!this.filterDoc ? null : (
() { multiSelect={true} onPointerDown={e => script && !toggleStatus && setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => script.run({ this: this.Document, value: undefined, _readOnly_: false }))} toggleStatus={toggleStatus} + showUntilToggle={BoolCast(this.Document.showUntilToggle)} // allow the toggle to be clickable unless ignoreClick is set on the Document label={selectedItems.length === 1 ? selectedItems[0] : this.label} items={items.map(item => ({ icon: , diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index d90be007f..341340363 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1761,7 +1761,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { for (let newFocusEle = target instanceof HTMLElement ? target : null; newFocusEle; newFocusEle = newFocusEle?.parentElement) { // test if parent of new focused element is a UI button (should be more specific than testing className) - if (newFocusEle?.className === 'fonticonbox' || newFocusEle?.className === 'popup-container') { + if (['fonticonbox', 'antimodeMenu-cont', 'popup-container'].includes(newFocusEle?.className ?? '')) { return this.EditorView?.focus(); // keep focus on text box } } diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 758b4035e..10c95f1e1 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -367,7 +367,8 @@ export class RichTextMenu extends AntimodeMenu { setFontField = (value: string, fontField: 'fitBox' | 'fontSize' | 'fontFamily' | 'fontColor' | 'fontHighlight') => { if (this.TextView && this.view && fontField !== 'fitBox') { - if (this.view.hasFocus()) { + const anchorNode = window.getSelection()?.anchorNode; + if (this.view.hasFocus() || (anchorNode && this.TextView.ProseRef?.contains(anchorNode))) { const attrs: { [key: string]: string } = {}; attrs[fontField] = value; const fmark = this.view.state.schema.marks['pF' + fontField.substring(1)].create(attrs); -- cgit v1.2.3-70-g09d2