mirror of
https://github.com/zadam/trilium.git
synced 2026-02-22 13:44:25 +01:00
Further mobile improvements (#8769)
This commit is contained in:
commit
bc89bc8270
@ -23,8 +23,6 @@ import NoteTreeWidget from "../widgets/note_tree.js";
|
||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||
import NoteDetail from "../widgets/NoteDetail.jsx";
|
||||
import QuickSearchWidget from "../widgets/quick_search.js";
|
||||
import { useNoteContext } from "../widgets/react/hooks.jsx";
|
||||
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
|
||||
import ScrollPadding from "../widgets/scroll_padding";
|
||||
import SearchResult from "../widgets/search_result.jsx";
|
||||
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
|
||||
|
||||
@ -1587,7 +1587,7 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
bottom: 0;
|
||||
height: 100dvh;
|
||||
width: 85vw;
|
||||
padding-top: env(safe-area-inset-top);
|
||||
transition: transform 250ms ease-in-out;
|
||||
@ -1651,13 +1651,27 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
body.mobile .jump-to-note-dialog .modal-content {
|
||||
overflow-y: auto;
|
||||
}
|
||||
body.mobile .jump-to-note-dialog {
|
||||
.modal-header {
|
||||
padding-bottom: 0.75rem !important;
|
||||
}
|
||||
|
||||
body.mobile .jump-to-note-dialog .modal-dialog .aa-dropdown-menu {
|
||||
max-height: unset;
|
||||
overflow: auto;
|
||||
.modal-content {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.aa-dropdown-menu {
|
||||
max-height: unset;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.aa-suggestion {
|
||||
padding-inline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
body.mobile .modal-dialog .dropdown-menu {
|
||||
|
||||
@ -47,6 +47,7 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
|
||||
>
|
||||
{isMobile() && <>
|
||||
<MenuItem command="searchNotes" icon="bx bx-search" text={t("global_menu.search_notes")} />
|
||||
<MenuItem command="showRecentChanges" icon="bx bx-history" text={t("recent_changes.title")} />
|
||||
<FormDropdownDivider />
|
||||
</>}
|
||||
|
||||
|
||||
@ -3,11 +3,14 @@ import { createContext } from "preact";
|
||||
import { useContext } from "preact/hooks";
|
||||
|
||||
import FNote from "../../entities/fnote";
|
||||
import utils from "../../services/utils";
|
||||
import ActionButton, { ActionButtonProps } from "../react/ActionButton";
|
||||
import Dropdown, { DropdownProps } from "../react/Dropdown";
|
||||
import { useNoteLabel, useNoteProperty } from "../react/hooks";
|
||||
import Icon from "../react/Icon";
|
||||
|
||||
const cachedIsMobile = utils.isMobile();
|
||||
|
||||
export const LaunchBarContext = createContext<{
|
||||
isHorizontalLayout: boolean;
|
||||
}>({
|
||||
@ -26,7 +29,7 @@ export function LaunchBarActionButton({ className, ...props }: Omit<ActionButton
|
||||
<ActionButton
|
||||
className={clsx("button-widget launcher-button", className)}
|
||||
noIconActionClass
|
||||
titlePosition={isHorizontalLayout ? "bottom" : "right"}
|
||||
titlePosition={getTitlePosition(isHorizontalLayout)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@ -34,6 +37,7 @@ export function LaunchBarActionButton({ className, ...props }: Omit<ActionButton
|
||||
|
||||
export function LaunchBarDropdownButton({ children, icon, dropdownOptions, ...props }: Pick<DropdownProps, "title" | "children" | "onShown" | "dropdownOptions" | "dropdownRef"> & { icon: string }) {
|
||||
const { isHorizontalLayout } = useContext(LaunchBarContext);
|
||||
const titlePosition = getTitlePosition(isHorizontalLayout);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@ -41,12 +45,12 @@ export function LaunchBarDropdownButton({ children, icon, dropdownOptions, ...pr
|
||||
buttonClassName="right-dropdown-button launcher-button"
|
||||
hideToggleArrow
|
||||
text={<Icon icon={icon} />}
|
||||
titlePosition={isHorizontalLayout ? "bottom" : "right"}
|
||||
titlePosition={titlePosition}
|
||||
titleOptions={{ animation: false }}
|
||||
dropdownOptions={{
|
||||
...dropdownOptions,
|
||||
popperConfig: {
|
||||
placement: isHorizontalLayout ? "bottom" : "right"
|
||||
placement: titlePosition
|
||||
}
|
||||
}}
|
||||
mobileBackdrop
|
||||
@ -67,3 +71,10 @@ export function useLauncherIconAndTitle(note: FNote) {
|
||||
title: title ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
function getTitlePosition(isHorizontalLayout: boolean) {
|
||||
if (cachedIsMobile) {
|
||||
return "top";
|
||||
}
|
||||
return isHorizontalLayout ? "bottom" : "right";
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ const DRAG_OPEN_THRESHOLD = 10;
|
||||
/** The number of pixels the user has to drag across the screen to the right when the sidebar is closed to trigger the drag open animation. */
|
||||
const DRAG_CLOSED_START_THRESHOLD = 10;
|
||||
/** The number of pixels the user has to drag across the screen to the left when the sidebar is opened to trigger the drag close animation. */
|
||||
const DRAG_OPENED_START_THRESHOLD = 80;
|
||||
const DRAG_OPENED_START_THRESHOLD = 100;
|
||||
|
||||
export default class SidebarContainer extends FlexContainer<BasicWidget> {
|
||||
private screenName: Screen;
|
||||
@ -54,7 +54,7 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
|
||||
this.startX = x;
|
||||
|
||||
// Prevent dragging if too far from the edge of the screen and the menu is closed.
|
||||
let dragRefX = glob.isRtl ? this.screenWidth - x : x;
|
||||
const dragRefX = glob.isRtl ? this.screenWidth - x : x;
|
||||
if (dragRefX > 30 && this.currentTranslate === -100) {
|
||||
return;
|
||||
}
|
||||
@ -89,7 +89,7 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
|
||||
}
|
||||
} else if (this.dragState === DRAG_STATE_DRAGGING) {
|
||||
const width = this.sidebarEl.offsetWidth;
|
||||
let translatePercentage = Math.min(0, Math.max(this.currentTranslate + (deltaX / width) * 100, -100));
|
||||
const translatePercentage = Math.min(0, Math.max(this.currentTranslate + (deltaX / width) * 100, -100));
|
||||
const backdropOpacity = Math.max(0, 1 + translatePercentage / 100);
|
||||
this.translatePercentage = translatePercentage;
|
||||
if (glob.isRtl) {
|
||||
@ -160,12 +160,10 @@ export default class SidebarContainer extends FlexContainer<BasicWidget> {
|
||||
this.sidebarEl.classList.toggle("show", isOpen);
|
||||
if (isOpen) {
|
||||
this.sidebarEl.style.transform = "translateX(0)";
|
||||
} else if (glob.isRtl) {
|
||||
this.sidebarEl.style.transform = "translateX(100%)";
|
||||
} else {
|
||||
if (glob.isRtl) {
|
||||
this.sidebarEl.style.transform = "translateX(100%)"
|
||||
} else {
|
||||
this.sidebarEl.style.transform = "translateX(-100%)";
|
||||
}
|
||||
this.sidebarEl.style.transform = "translateX(-100%)";
|
||||
}
|
||||
this.sidebarEl.style.transition = this.originalSidebarTransition;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import { t } from "../../services/i18n";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import { useNoteContext } from "../react/hooks";
|
||||
|
||||
export default function ToggleSidebarButton() {
|
||||
@ -10,10 +10,15 @@ export default function ToggleSidebarButton() {
|
||||
{ noteContext?.isMainContext() && <ActionButton
|
||||
icon="bx bx-sidebar"
|
||||
text={t("note_tree.toggle-sidebar")}
|
||||
onClick={() => parentComponent?.triggerCommand("setActiveScreen", {
|
||||
screen: "tree"
|
||||
})}
|
||||
onClick={(e) => {
|
||||
// Remove focus to prevent tooltip showing on top of the sidebar.
|
||||
(e.currentTarget as HTMLButtonElement).blur();
|
||||
|
||||
parentComponent?.triggerCommand("setActiveScreen", {
|
||||
screen: "tree"
|
||||
});
|
||||
}}
|
||||
/>}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ function MobileNoteIconSwitcher({ note, icon }: {
|
||||
const [ modalShown, setModalShown ] = useState(false);
|
||||
const { windowWidth } = useWindowSize();
|
||||
|
||||
return (note &&
|
||||
return (
|
||||
<div className="note-icon-widget">
|
||||
<ActionButton
|
||||
className="note-icon"
|
||||
@ -86,7 +86,7 @@ function MobileNoteIconSwitcher({ note, icon }: {
|
||||
className="icon-switcher note-icon-widget"
|
||||
scrollable
|
||||
>
|
||||
<NoteIconList note={note} onHide={() => setModalShown(false)} columnCount={Math.max(1, Math.floor(windowWidth / ICON_SIZE))} />
|
||||
{note && <NoteIconList note={note} onHide={() => setModalShown(false)} columnCount={Math.max(1, Math.floor(windowWidth / ICON_SIZE))} />}
|
||||
</Modal>
|
||||
), document.body)}
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
import { CommandNames } from "../../components/app_context";
|
||||
import keyboard_actions from "../../services/keyboard_actions";
|
||||
import { isMobile } from "../../services/utils";
|
||||
import { useStaticTooltip } from "./hooks";
|
||||
|
||||
export interface ActionButtonProps extends Pick<HTMLAttributes<HTMLButtonElement>, "onClick" | "onAuxClick" | "onContextMenu" | "style"> {
|
||||
@ -17,6 +18,8 @@ export interface ActionButtonProps extends Pick<HTMLAttributes<HTMLButtonElement
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const cachedIsMobile = isMobile();
|
||||
|
||||
export default function ActionButton({ text, icon, className, triggerCommand, titlePosition, noIconActionClass, frame, active, disabled, ...restProps }: ActionButtonProps) {
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
const [ keyboardShortcut, setKeyboardShortcut ] = useState<string[]>();
|
||||
@ -25,6 +28,7 @@ export default function ActionButton({ text, icon, className, triggerCommand, ti
|
||||
title: keyboardShortcut?.length ? `${text} (${keyboardShortcut?.join(",")})` : text,
|
||||
placement: titlePosition ?? "bottom",
|
||||
fallbackPlacements: [ titlePosition ?? "bottom" ],
|
||||
trigger: cachedIsMobile ? "focus" : "hover focus",
|
||||
animation: false
|
||||
});
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import type { ComponentChildren, RefObject } from "preact";
|
||||
import type { CSSProperties } from "preact/compat";
|
||||
import type { ComponentChildren, CSSProperties, RefObject } from "preact";
|
||||
import { memo } from "preact/compat";
|
||||
import { useMemo } from "preact/hooks";
|
||||
|
||||
import { CommandNames } from "../../components/app_context";
|
||||
import { isDesktop } from "../../services/utils";
|
||||
import { isDesktop, isMobile } from "../../services/utils";
|
||||
import ActionButton from "./ActionButton";
|
||||
import Icon from "./Icon";
|
||||
|
||||
const cachedIsMobile = isMobile();
|
||||
|
||||
export interface ButtonProps {
|
||||
name?: string;
|
||||
/** Reference to the button element. Mostly useful for requesting focus. */
|
||||
@ -30,7 +31,7 @@ const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortc
|
||||
// Memoize classes array to prevent recreation
|
||||
const classes = useMemo(() => {
|
||||
const classList: string[] = ["btn"];
|
||||
|
||||
|
||||
switch(kind) {
|
||||
case "primary":
|
||||
classList.push("btn-primary");
|
||||
@ -42,7 +43,7 @@ const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortc
|
||||
classList.push("btn-secondary");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (className) {
|
||||
classList.push(className);
|
||||
}
|
||||
@ -56,7 +57,7 @@ const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortc
|
||||
|
||||
// Memoize keyboard shortcut rendering
|
||||
const shortcutElements = useMemo(() => {
|
||||
if (!keyboardShortcut) return null;
|
||||
if (!keyboardShortcut || cachedIsMobile) return null;
|
||||
const splitShortcut = keyboardShortcut.split("+");
|
||||
return splitShortcut.map((key, index) => (
|
||||
<>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user