mirror of
https://github.com/zadam/trilium.git
synced 2025-12-12 18:34:24 +01:00
New layout: Integrate Basic properties (#8014)
Some checks are pending
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run
Some checks are pending
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run
This commit is contained in:
commit
19b32dd3a6
@ -52,6 +52,7 @@ import FormattingToolbar from "../widgets/ribbon/FormattingToolbar.jsx";
|
|||||||
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
||||||
import BreadcrumbBadges from "../widgets/BreadcrumbBadges.jsx";
|
import BreadcrumbBadges from "../widgets/BreadcrumbBadges.jsx";
|
||||||
import NoteTitleDetails from "../widgets/NoteTitleDetails.jsx";
|
import NoteTitleDetails from "../widgets/NoteTitleDetails.jsx";
|
||||||
|
import NoteStatusBar from "../widgets/NoteStatusBar.jsx";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
|
||||||
@ -176,7 +177,11 @@ export default class DesktopLayout {
|
|||||||
...this.customWidgets.get("node-detail-pane"), // typo, let's keep it for a while as BC
|
...this.customWidgets.get("node-detail-pane"), // typo, let's keep it for a while as BC
|
||||||
...this.customWidgets.get("note-detail-pane")
|
...this.customWidgets.get("note-detail-pane")
|
||||||
)
|
)
|
||||||
.optChild(isNewLayout, <Ribbon />)
|
.optChild(isNewLayout, (
|
||||||
|
<Ribbon>
|
||||||
|
<NoteStatusBar />
|
||||||
|
</Ribbon>
|
||||||
|
))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.child(...this.customWidgets.get("center-pane"))
|
.child(...this.customWidgets.get("center-pane"))
|
||||||
|
|||||||
@ -27,6 +27,16 @@ export function getEnabledExperimentalFeatureIds() {
|
|||||||
return getEnabledFeatures().values();
|
return getEnabledFeatures().values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function toggleExperimentalFeature(featureId: ExperimentalFeatureId, enable: boolean) {
|
||||||
|
const features = new Set(getEnabledFeatures());
|
||||||
|
if (enable) {
|
||||||
|
features.add(featureId);
|
||||||
|
} else {
|
||||||
|
features.delete(featureId);
|
||||||
|
}
|
||||||
|
await options.save("experimentalFeatures", JSON.stringify(Array.from(features)));
|
||||||
|
}
|
||||||
|
|
||||||
function getEnabledFeatures() {
|
function getEnabledFeatures() {
|
||||||
if (!enabledFeatures) {
|
if (!enabledFeatures) {
|
||||||
let features: ExperimentalFeatureId[] = [];
|
let features: ExperimentalFeatureId[] = [];
|
||||||
|
|||||||
@ -423,16 +423,16 @@ body.desktop .tabulator-popup-container,
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu .disabled .disabled-tooltip {
|
.dropdown-menu .disabled .contextual-help {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
margin-inline-start: 8px;
|
margin-inline-start: 8px;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--disabled-tooltip-icon-color);
|
color: var(--contextual-help-icon-color);
|
||||||
cursor: help;
|
cursor: help;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu .disabled .disabled-tooltip:hover {
|
.dropdown-menu .disabled .contextual-help:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1315,7 +1315,8 @@ body.desktop li.dropdown-submenu:hover > ul.dropdown-menu {
|
|||||||
top: 0;
|
top: 0;
|
||||||
inset-inline-start: calc(100% - 2px); /* -2px, otherwise there's a small gap between menu and submenu where the hover can disappear */
|
inset-inline-start: calc(100% - 2px); /* -2px, otherwise there's a small gap between menu and submenu where the hover can disappear */
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
min-width: 15rem;
|
min-width: max-content;
|
||||||
|
max-width: 300px;
|
||||||
/* to make submenu scrollable https://github.com/zadam/trilium/issues/3136 */
|
/* to make submenu scrollable https://github.com/zadam/trilium/issues/3136 */
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
--dropdown-border-color: #555;
|
--dropdown-border-color: #555;
|
||||||
--dropdown-shadow-opacity: 0.4;
|
--dropdown-shadow-opacity: 0.4;
|
||||||
--dropdown-item-icon-destructive-color: #de6e5b;
|
--dropdown-item-icon-destructive-color: #de6e5b;
|
||||||
--disabled-tooltip-icon-color: #7fd2ef;
|
--contextual-help-icon-color: #7fd2ef;
|
||||||
|
|
||||||
--accented-background-color: #555;
|
--accented-background-color: #555;
|
||||||
--more-accented-background-color: #777;
|
--more-accented-background-color: #777;
|
||||||
@ -114,4 +114,4 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
|
|||||||
|
|
||||||
.use-note-color {
|
.use-note-color {
|
||||||
--custom-color: var(--dark-theme-custom-color);
|
--custom-color: var(--dark-theme-custom-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ html {
|
|||||||
--dropdown-border-color: #ccc;
|
--dropdown-border-color: #ccc;
|
||||||
--dropdown-shadow-opacity: 0.2;
|
--dropdown-shadow-opacity: 0.2;
|
||||||
--dropdown-item-icon-destructive-color: #ec5138;
|
--dropdown-item-icon-destructive-color: #ec5138;
|
||||||
--disabled-tooltip-icon-color: #004382;
|
--contextual-help-icon-color: #004382;
|
||||||
|
|
||||||
--accented-background-color: #f5f5f5;
|
--accented-background-color: #f5f5f5;
|
||||||
--more-accented-background-color: #ddd;
|
--more-accented-background-color: #ddd;
|
||||||
@ -98,4 +98,4 @@ html {
|
|||||||
|
|
||||||
.use-note-color {
|
.use-note-color {
|
||||||
--custom-color: var(--light-theme-custom-color);
|
--custom-color: var(--light-theme-custom-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
:root {
|
:root {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ⚠️ NOTICE: This theme is currently in the beta stage of development.
|
* ⚠️ NOTICE: This theme is currently in the beta stage of development.
|
||||||
* The names and purposes of these CSS variables are subject to frequent changes.
|
* The names and purposes of these CSS variables are subject to frequent changes.
|
||||||
*/
|
*/
|
||||||
@ -22,7 +22,7 @@
|
|||||||
--dropdown-border-color: #404040;
|
--dropdown-border-color: #404040;
|
||||||
--dropdown-shadow-opacity: 0.6;
|
--dropdown-shadow-opacity: 0.6;
|
||||||
--dropdown-item-icon-destructive-color: #de6e5b;
|
--dropdown-item-icon-destructive-color: #de6e5b;
|
||||||
--disabled-tooltip-icon-color: #7fd2ef;
|
--contextual-help-icon-color: #7fd2ef;
|
||||||
|
|
||||||
--accented-background-color: #555;
|
--accented-background-color: #555;
|
||||||
|
|
||||||
@ -182,7 +182,7 @@
|
|||||||
|
|
||||||
--tab-close-button-hover-background: #a45353;
|
--tab-close-button-hover-background: #a45353;
|
||||||
--tab-close-button-hover-color: white;
|
--tab-close-button-hover-color: white;
|
||||||
|
|
||||||
--active-tab-background-color: #ffffff1c;
|
--active-tab-background-color: #ffffff1c;
|
||||||
--active-tab-hover-background-color: var(--active-tab-background-color);
|
--active-tab-hover-background-color: var(--active-tab-background-color);
|
||||||
--active-tab-icon-color: #a9a9a9;
|
--active-tab-icon-color: #a9a9a9;
|
||||||
@ -201,7 +201,7 @@
|
|||||||
|
|
||||||
--promoted-attribute-card-background-color: #ffffff21;
|
--promoted-attribute-card-background-color: #ffffff21;
|
||||||
--promoted-attribute-card-shadow: none;
|
--promoted-attribute-card-shadow: none;
|
||||||
|
|
||||||
--floating-button-shadow-color: #00000080;
|
--floating-button-shadow-color: #00000080;
|
||||||
--floating-button-background-color: #494949d2;
|
--floating-button-background-color: #494949d2;
|
||||||
--floating-button-color: var(--button-text-color);
|
--floating-button-color: var(--button-text-color);
|
||||||
@ -226,7 +226,7 @@
|
|||||||
--scrollbar-border-color: unset; /* Deprecated */
|
--scrollbar-border-color: unset; /* Deprecated */
|
||||||
|
|
||||||
--selection-background-color: #3399FF70;
|
--selection-background-color: #3399FF70;
|
||||||
|
|
||||||
--link-color: lightskyblue;
|
--link-color: lightskyblue;
|
||||||
|
|
||||||
--mermaid-theme: dark;
|
--mermaid-theme: dark;
|
||||||
@ -320,4 +320,4 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
|
|||||||
|
|
||||||
.use-note-color {
|
.use-note-color {
|
||||||
--custom-color: var(--dark-theme-custom-color);
|
--custom-color: var(--dark-theme-custom-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
:root {
|
:root {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ⚠️ NOTICE: This theme is currently in the beta stage of development.
|
* ⚠️ NOTICE: This theme is currently in the beta stage of development.
|
||||||
* The names and purposes of these CSS variables are subject to frequent changes.
|
* The names and purposes of these CSS variables are subject to frequent changes.
|
||||||
*/
|
*/
|
||||||
@ -22,7 +22,7 @@
|
|||||||
--dropdown-border-color: #ccc;
|
--dropdown-border-color: #ccc;
|
||||||
--dropdown-shadow-opacity: 0.2;
|
--dropdown-shadow-opacity: 0.2;
|
||||||
--dropdown-item-icon-destructive-color: #ec5138;
|
--dropdown-item-icon-destructive-color: #ec5138;
|
||||||
--disabled-tooltip-icon-color: #004382;
|
--contextual-help-icon-color: #004382;
|
||||||
|
|
||||||
--accented-background-color: #f5f5f5;
|
--accented-background-color: #f5f5f5;
|
||||||
|
|
||||||
@ -138,7 +138,7 @@
|
|||||||
/* Deprecated: now local variables in #launcher, with the values dependent on the current layout. */
|
/* Deprecated: now local variables in #launcher, with the values dependent on the current layout. */
|
||||||
--launcher-pane-background-color: unset;
|
--launcher-pane-background-color: unset;
|
||||||
--launcher-pane-text-color: unset;
|
--launcher-pane-text-color: unset;
|
||||||
|
|
||||||
--launcher-pane-vert-background-color: #e8e8e8;
|
--launcher-pane-vert-background-color: #e8e8e8;
|
||||||
--launcher-pane-vert-text-color: #000000bd;
|
--launcher-pane-vert-text-color: #000000bd;
|
||||||
--launcher-pane-vert-button-hover-color: black;
|
--launcher-pane-vert-button-hover-color: black;
|
||||||
@ -174,7 +174,7 @@
|
|||||||
|
|
||||||
--tab-close-button-hover-background: #c95a5a;
|
--tab-close-button-hover-background: #c95a5a;
|
||||||
--tab-close-button-hover-color: white;
|
--tab-close-button-hover-color: white;
|
||||||
|
|
||||||
--active-tab-background-color: white;
|
--active-tab-background-color: white;
|
||||||
--active-tab-hover-background-color: var(--active-tab-background-color);
|
--active-tab-hover-background-color: var(--active-tab-background-color);
|
||||||
--active-tab-icon-color: gray;
|
--active-tab-icon-color: gray;
|
||||||
@ -291,4 +291,4 @@
|
|||||||
--modal-background-color: hsl(var(--custom-color-hue), 56%, 96%);
|
--modal-background-color: hsl(var(--custom-color-hue), 56%, 96%);
|
||||||
--modal-border-color: hsl(var(--custom-color-hue), 33%, 41%);
|
--modal-border-color: hsl(var(--custom-color-hue), 33%, 41%);
|
||||||
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
|
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1962,8 +1962,9 @@
|
|||||||
"unknown_widget": "Unknown widget for \"{{id}}\"."
|
"unknown_widget": "Unknown widget for \"{{id}}\"."
|
||||||
},
|
},
|
||||||
"note_language": {
|
"note_language": {
|
||||||
"not_set": "Not set",
|
"not_set": "No language set",
|
||||||
"configure-languages": "Configure languages..."
|
"configure-languages": "Configure languages...",
|
||||||
|
"help-on-languages": "Help on content languages..."
|
||||||
},
|
},
|
||||||
"content_language": {
|
"content_language": {
|
||||||
"title": "Content languages",
|
"title": "Content languages",
|
||||||
|
|||||||
@ -52,7 +52,7 @@ function ShareBadge() {
|
|||||||
|
|
||||||
return (link &&
|
return (link &&
|
||||||
<Badge
|
<Badge
|
||||||
icon={isSharedExternally ? "bx bx-world" : "bx bx-link"}
|
icon={isSharedExternally ? "bx bx-world" : "bx bx-share-alt"}
|
||||||
text={isSharedExternally ? t("breadcrumb_badges.shared_publicly") : t("breadcrumb_badges.shared_locally")}
|
text={isSharedExternally ? t("breadcrumb_badges.shared_publicly") : t("breadcrumb_badges.shared_locally")}
|
||||||
tooltip={isSharedExternally ?
|
tooltip={isSharedExternally ?
|
||||||
t("breadcrumb_badges.shared_publicly_description", { link }) :
|
t("breadcrumb_badges.shared_publicly_description", { link }) :
|
||||||
|
|||||||
13
apps/client/src/widgets/NoteStatusBar.css
Normal file
13
apps/client/src/widgets/NoteStatusBar.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.note-status-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-inline: 1em;
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
font-size: 0.85em;
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
padding: 0.1em 0.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
apps/client/src/widgets/NoteStatusBar.tsx
Normal file
25
apps/client/src/widgets/NoteStatusBar.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import "./NoteStatusBar.css";
|
||||||
|
|
||||||
|
import { t } from "../services/i18n";
|
||||||
|
import { openInAppHelpFromUrl } from "../services/utils";
|
||||||
|
import { FormListItem } from "./react/FormList";
|
||||||
|
import { useNoteContext } from "./react/hooks";
|
||||||
|
import { NoteLanguageSelector } from "./ribbon/BasicPropertiesTab";
|
||||||
|
|
||||||
|
export default function NoteStatusBar() {
|
||||||
|
const { note } = useNoteContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="note-status-bar">
|
||||||
|
<NoteLanguageSelector
|
||||||
|
note={note}
|
||||||
|
extraChildren={(
|
||||||
|
<FormListItem
|
||||||
|
onClick={() => openInAppHelpFromUrl("veGu4faJErEM")}
|
||||||
|
icon="bx bx-help-circle"
|
||||||
|
>{t("note_language.help-on-languages")}</FormListItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -10,7 +10,8 @@ import { KeyboardActionNames } from "@triliumnext/commons";
|
|||||||
import { ComponentChildren } from "preact";
|
import { ComponentChildren } from "preact";
|
||||||
import Component from "../../components/component";
|
import Component from "../../components/component";
|
||||||
import { ParentComponent } from "../react/react_utils";
|
import { ParentComponent } from "../react/react_utils";
|
||||||
import utils, { dynamicRequire, isElectron, isMobile } from "../../services/utils";
|
import utils, { dynamicRequire, isElectron, isMobile, reloadFrontendApp } from "../../services/utils";
|
||||||
|
import { isExperimentalFeatureEnabled, toggleExperimentalFeature } from "../../services/experimental_features";
|
||||||
|
|
||||||
interface MenuItemProps<T> {
|
interface MenuItemProps<T> {
|
||||||
icon: string,
|
icon: string,
|
||||||
@ -70,6 +71,7 @@ export default function GlobalMenu({ isHorizontalLayout }: { isHorizontalLayout:
|
|||||||
</>}
|
</>}
|
||||||
|
|
||||||
{!isElectron() && <BrowserOnlyOptions />}
|
{!isElectron() && <BrowserOnlyOptions />}
|
||||||
|
{glob.isDev && <DevelopmentOptions />}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -99,6 +101,21 @@ function BrowserOnlyOptions() {
|
|||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DevelopmentOptions() {
|
||||||
|
const newLayoutEnabled = isExperimentalFeatureEnabled("new-layout");
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<FormDropdownDivider />
|
||||||
|
<FormListItem
|
||||||
|
icon={newLayoutEnabled ? "bx bx-layout" : "bx bxs-layout"}
|
||||||
|
onClick={async () => {
|
||||||
|
await toggleExperimentalFeature("new-layout", !newLayoutEnabled);
|
||||||
|
reloadFrontendApp();
|
||||||
|
}}
|
||||||
|
>{!newLayoutEnabled ? "Switch to new layout" : "Switch to old layout"}</FormListItem>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
function SwitchToOptions() {
|
function SwitchToOptions() {
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -1,9 +1,29 @@
|
|||||||
.dropdown-item .description {
|
.dropdown-item {
|
||||||
font-size: small;
|
.description {
|
||||||
color: var(--muted-text-color);
|
font-size: small;
|
||||||
white-space: normal;
|
color: var(--muted-text-color);
|
||||||
}
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-item span.bx {
|
span.bx {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switch-widget {
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
--switch-track-width: 40px;
|
||||||
|
--switch-track-height: 20px;
|
||||||
|
--switch-thumb-width: 12px;
|
||||||
|
--switch-thumb-height: var(--switch-thumb-width);
|
||||||
|
|
||||||
|
.contextual-help {
|
||||||
|
margin-inline-start: 0.25em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties } from "preact
|
|||||||
import "./FormList.css";
|
import "./FormList.css";
|
||||||
import { CommandNames } from "../../components/app_context";
|
import { CommandNames } from "../../components/app_context";
|
||||||
import { useStaticTooltip } from "./hooks";
|
import { useStaticTooltip } from "./hooks";
|
||||||
import { handleRightToLeftPlacement, isMobile } from "../../services/utils";
|
import { handleRightToLeftPlacement, isMobile, openInAppHelpFromUrl } from "../../services/utils";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import FormToggle from "./FormToggle";
|
||||||
|
|
||||||
interface FormListOpts {
|
interface FormListOpts {
|
||||||
children: ComponentChildren;
|
children: ComponentChildren;
|
||||||
@ -94,12 +95,13 @@ interface FormListItemOpts {
|
|||||||
description?: string;
|
description?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
rtl?: boolean;
|
rtl?: boolean;
|
||||||
|
postContent?: ComponentChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
|
const TOOLTIP_CONFIG: Partial<Tooltip.Options> = {
|
||||||
placement: handleRightToLeftPlacement("right"),
|
placement: handleRightToLeftPlacement("right"),
|
||||||
fallbackPlacements: [ handleRightToLeftPlacement("right") ]
|
fallbackPlacements: [ handleRightToLeftPlacement("right") ]
|
||||||
}
|
};
|
||||||
|
|
||||||
export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) {
|
export function FormListItem({ className, icon, value, title, active, disabled, checked, container, onClick, selected, rtl, triggerCommand, description, ...contentProps }: FormListItemOpts) {
|
||||||
const itemRef = useRef<HTMLLIElement>(null);
|
const itemRef = useRef<HTMLLIElement>(null);
|
||||||
@ -132,6 +134,49 @@ export function FormListItem({ className, icon, value, title, active, disabled,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FormListToggleableItem({ title, currentValue, onChange, disabled, helpPage, ...props }: Omit<FormListItemOpts, "onClick" | "children"> & {
|
||||||
|
title: string;
|
||||||
|
currentValue: boolean;
|
||||||
|
helpPage?: string;
|
||||||
|
onChange(newValue: boolean): void | Promise<void>;
|
||||||
|
}) {
|
||||||
|
const isWaiting = useRef(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormListItem
|
||||||
|
{...props}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={async (e) => {
|
||||||
|
if ((e.target as HTMLElement | null)?.classList.contains("contextual-help")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!disabled && !isWaiting.current) {
|
||||||
|
isWaiting.current = true;
|
||||||
|
await onChange(!currentValue);
|
||||||
|
isWaiting.current = false;
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<FormToggle
|
||||||
|
switchOnName={title}
|
||||||
|
switchOffName={title}
|
||||||
|
currentValue={currentValue}
|
||||||
|
onChange={() => {}}
|
||||||
|
afterName={<>
|
||||||
|
{helpPage && (
|
||||||
|
<span
|
||||||
|
class="bx bx-help-circle contextual-help"
|
||||||
|
onClick={() => openInAppHelpFromUrl(helpPage)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span class="switch-spacer" />
|
||||||
|
</>}
|
||||||
|
/>
|
||||||
|
</FormListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function FormListContent({ children, badges, description, disabled, disabledTooltip }: Pick<FormListItemOpts, "children" | "badges" | "description" | "disabled" | "disabledTooltip">) {
|
function FormListContent({ children, badges, description, disabled, disabledTooltip }: Pick<FormListItemOpts, "children" | "badges" | "description" | "disabled" | "disabledTooltip">) {
|
||||||
return <>
|
return <>
|
||||||
{children}
|
{children}
|
||||||
@ -139,7 +184,7 @@ function FormListContent({ children, badges, description, disabled, disabledTool
|
|||||||
<span className={`badge ${className ?? ""}`}>{text}</span>
|
<span className={`badge ${className ?? ""}`}>{text}</span>
|
||||||
))}
|
))}
|
||||||
{disabled && disabledTooltip && (
|
{disabled && disabledTooltip && (
|
||||||
<span class="bx bx-info-circle disabled-tooltip" title={disabledTooltip} />
|
<span class="bx bx-info-circle contextual-help" title={disabledTooltip} />
|
||||||
)}
|
)}
|
||||||
{description && <div className="description">{description}</div>}
|
{description && <div className="description">{description}</div>}
|
||||||
</>;
|
</>;
|
||||||
|
|||||||
@ -24,6 +24,14 @@
|
|||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
background-color: var(--switch-off-track-background);
|
background-color: var(--switch-off-track-background);
|
||||||
transition: background 200ms ease-in;
|
transition: background 200ms ease-in;
|
||||||
|
|
||||||
|
&.disable-transitions {
|
||||||
|
transition: none !important;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch-widget .switch-button.on {
|
.switch-widget .switch-button.on {
|
||||||
@ -103,4 +111,4 @@ body[dir=rtl] .switch-widget .switch-button.on:after {
|
|||||||
|
|
||||||
.switch-widget .switch-help-button:hover {
|
.switch-widget .switch-help-button:hover {
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,39 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
import "./FormToggle.css";
|
import "./FormToggle.css";
|
||||||
import HelpButton from "./HelpButton";
|
import HelpButton from "./HelpButton";
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import { ComponentChildren } from "preact";
|
||||||
|
|
||||||
interface FormToggleProps {
|
interface FormToggleProps {
|
||||||
currentValue: boolean | null;
|
currentValue: boolean | null;
|
||||||
onChange(newValue: boolean): void;
|
onChange(newValue: boolean): void;
|
||||||
switchOnName: string;
|
switchOnName: string;
|
||||||
switchOnTooltip: string;
|
switchOnTooltip?: string;
|
||||||
switchOffName: string;
|
switchOffName: string;
|
||||||
switchOffTooltip: string;
|
switchOffTooltip?: string;
|
||||||
helpPage?: string;
|
helpPage?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
afterName?: ComponentChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FormToggle({ currentValue, helpPage, switchOnName, switchOnTooltip, switchOffName, switchOffTooltip, onChange, disabled }: FormToggleProps) {
|
export default function FormToggle({ currentValue, helpPage, switchOnName, switchOnTooltip, switchOffName, switchOffTooltip, onChange, disabled, afterName }: FormToggleProps) {
|
||||||
|
const [ disableTransition, setDisableTransition ] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
setDisableTransition(false);
|
||||||
|
}, 100);
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="switch-widget">
|
<div className="switch-widget">
|
||||||
<span className="switch-name">{ currentValue ? switchOffName : switchOnName }</span>
|
<span className="switch-name">{ currentValue ? switchOffName : switchOnName }</span>
|
||||||
|
{ afterName }
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<div
|
<div
|
||||||
className={`switch-button ${currentValue ? "on" : ""} ${disabled ? "disabled" : ""}`}
|
className={clsx("switch-button", { "on": currentValue, disabled, "disable-transitions": disableTransition })}
|
||||||
title={currentValue ? switchOffTooltip : switchOnTooltip }
|
title={currentValue ? switchOffTooltip : switchOnTooltip }
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@ -37,5 +51,5 @@ export default function FormToggle({ currentValue, helpPage, switchOnName, switc
|
|||||||
|
|
||||||
{ helpPage && <HelpButton className="switch-help-button" helpPage={helpPage} />}
|
{ helpPage && <HelpButton className="switch-help-button" helpPage={helpPage} />}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -845,6 +845,8 @@ export function useGlobalShortcut(keyboardShortcut: string | null | undefined, h
|
|||||||
*/
|
*/
|
||||||
export function useIsNoteReadOnly(note: FNote | null | undefined, noteContext: NoteContext | undefined) {
|
export function useIsNoteReadOnly(note: FNote | null | undefined, noteContext: NoteContext | undefined) {
|
||||||
const [ isReadOnly, setIsReadOnly ] = useState<boolean | undefined>(undefined);
|
const [ isReadOnly, setIsReadOnly ] = useState<boolean | undefined>(undefined);
|
||||||
|
const [ readOnlyAttr ] = useNoteLabelBoolean(note, "readOnly");
|
||||||
|
const [ autoReadOnlyDisabledAttr ] = useNoteLabelBoolean(note, "autoReadOnlyDisabled");
|
||||||
|
|
||||||
const enableEditing = useCallback((enabled = true) => {
|
const enableEditing = useCallback((enabled = true) => {
|
||||||
if (noteContext?.viewScope) {
|
if (noteContext?.viewScope) {
|
||||||
@ -859,7 +861,7 @@ export function useIsNoteReadOnly(note: FNote | null | undefined, noteContext: N
|
|||||||
setIsReadOnly(readOnly);
|
setIsReadOnly(readOnly);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [ note, noteContext, noteContext?.viewScope ]);
|
}, [ note, noteContext, noteContext?.viewScope, readOnlyAttr, autoReadOnlyDisabledAttr ]);
|
||||||
|
|
||||||
useTriliumEvent("readOnlyTemporarilyDisabled", ({noteContext: eventNoteContext}) => {
|
useTriliumEvent("readOnlyTemporarilyDisabled", ({noteContext: eventNoteContext}) => {
|
||||||
if (noteContext?.ntxId === eventNoteContext.ntxId) {
|
if (noteContext?.ntxId === eventNoteContext.ntxId) {
|
||||||
|
|||||||
@ -1,26 +1,29 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "preact/hooks";
|
import { NoteType, ToggleInParentResponse } from "@triliumnext/commons";
|
||||||
import Dropdown from "../react/Dropdown";
|
import { ComponentChildren } from "preact";
|
||||||
import { NOTE_TYPES } from "../../services/note_types";
|
import { createPortal } from "preact/compat";
|
||||||
import { FormDropdownDivider, FormListBadge, FormListItem } from "../react/FormList";
|
import { Dispatch, StateUpdater, useCallback, useEffect, useMemo, useState } from "preact/hooks";
|
||||||
import { getAvailableLocales, t } from "../../services/i18n";
|
|
||||||
import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumOption } from "../react/hooks";
|
|
||||||
import mime_types from "../../services/mime_types";
|
|
||||||
import { Locale, LOCALES, NoteType, ToggleInParentResponse } from "@triliumnext/commons";
|
|
||||||
import server from "../../services/server";
|
|
||||||
import dialog from "../../services/dialog";
|
|
||||||
import FormToggle from "../react/FormToggle";
|
|
||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
import protected_session from "../../services/protected_session";
|
|
||||||
import FormDropdownList from "../react/FormDropdownList";
|
|
||||||
import toast from "../../services/toast";
|
|
||||||
import branches from "../../services/branches";
|
import branches from "../../services/branches";
|
||||||
|
import dialog from "../../services/dialog";
|
||||||
|
import { getAvailableLocales, t } from "../../services/i18n";
|
||||||
|
import mime_types from "../../services/mime_types";
|
||||||
|
import { NOTE_TYPES } from "../../services/note_types";
|
||||||
|
import protected_session from "../../services/protected_session";
|
||||||
|
import server from "../../services/server";
|
||||||
import sync from "../../services/sync";
|
import sync from "../../services/sync";
|
||||||
|
import toast from "../../services/toast";
|
||||||
|
import Dropdown from "../react/Dropdown";
|
||||||
|
import FormDropdownList from "../react/FormDropdownList";
|
||||||
|
import { FormDropdownDivider, FormListBadge, FormListItem } from "../react/FormList";
|
||||||
|
import FormToggle from "../react/FormToggle";
|
||||||
import HelpButton from "../react/HelpButton";
|
import HelpButton from "../react/HelpButton";
|
||||||
import { TabContext } from "./ribbon-interface";
|
import { useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumOption } from "../react/hooks";
|
||||||
import Modal from "../react/Modal";
|
import Modal from "../react/Modal";
|
||||||
import { CodeMimeTypesList } from "../type_widgets/options/code_notes";
|
import { CodeMimeTypesList } from "../type_widgets/options/code_notes";
|
||||||
import { ContentLanguagesList } from "../type_widgets/options/i18n";
|
|
||||||
import { LocaleSelector } from "../type_widgets/options/components/LocaleSelector";
|
import { LocaleSelector } from "../type_widgets/options/components/LocaleSelector";
|
||||||
|
import { ContentLanguagesList } from "../type_widgets/options/i18n";
|
||||||
|
import { TabContext } from "./ribbon-interface";
|
||||||
|
|
||||||
export default function BasicPropertiesTab({ note }: TabContext) {
|
export default function BasicPropertiesTab({ note }: TabContext) {
|
||||||
return (
|
return (
|
||||||
@ -37,18 +40,38 @@ export default function BasicPropertiesTab({ note }: TabContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function NoteTypeWidget({ note }: { note?: FNote | null }) {
|
function NoteTypeWidget({ note }: { note?: FNote | null }) {
|
||||||
const noteTypes = useMemo(() => NOTE_TYPES.filter((nt) => !nt.reserved && !nt.static), []);
|
|
||||||
const [ codeNotesMimeTypes ] = useTriliumOption("codeNotesMimeTypes");
|
|
||||||
const mimeTypes = useMemo(() => {
|
|
||||||
mime_types.loadMimeTypes();
|
|
||||||
return mime_types.getMimeTypes().filter(mimeType => mimeType.enabled)
|
|
||||||
}, [ codeNotesMimeTypes ]);
|
|
||||||
const notSelectableNoteTypes = useMemo(() => NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type), []);
|
const notSelectableNoteTypes = useMemo(() => NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type), []);
|
||||||
|
|
||||||
const currentNoteType = useNoteProperty(note, "type") ?? undefined;
|
const currentNoteType = useNoteProperty(note, "type") ?? undefined;
|
||||||
const currentNoteMime = useNoteProperty(note, "mime");
|
const currentNoteMime = useNoteProperty(note, "mime");
|
||||||
const [ modalShown, setModalShown ] = useState(false);
|
const [ modalShown, setModalShown ] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="note-type-container">
|
||||||
|
<span>{t("basic_properties.note_type")}:</span>
|
||||||
|
<Dropdown
|
||||||
|
dropdownContainerClassName="note-type-dropdown"
|
||||||
|
text={<span className="note-type-desc">{findTypeTitle(currentNoteType, currentNoteMime)}</span>}
|
||||||
|
disabled={notSelectableNoteTypes.includes(currentNoteType ?? "text")}
|
||||||
|
>
|
||||||
|
<NoteTypeDropdownContent currentNoteType={currentNoteType} currentNoteMime={currentNoteMime} note={note} setModalShown={setModalShown} />
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
|
{createPortal(
|
||||||
|
<NoteTypeOptionsModal modalShown={modalShown} setModalShown={setModalShown} />,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NoteTypeDropdownContent({ currentNoteType, currentNoteMime, note, setModalShown }: { currentNoteType?: NoteType, currentNoteMime?: string | null, note?: FNote | null, setModalShown: Dispatch<StateUpdater<boolean>> }) {
|
||||||
|
const [ codeNotesMimeTypes ] = useTriliumOption("codeNotesMimeTypes");
|
||||||
|
const noteTypes = useMemo(() => NOTE_TYPES.filter((nt) => !nt.reserved && !nt.static), []);
|
||||||
|
const mimeTypes = useMemo(() => {
|
||||||
|
mime_types.loadMimeTypes();
|
||||||
|
return mime_types.getMimeTypes().filter(mimeType => mimeType.enabled);
|
||||||
|
}, [ codeNotesMimeTypes ]);
|
||||||
const changeNoteType = useCallback(async (type: NoteType, mime?: string) => {
|
const changeNoteType = useCallback(async (type: NoteType, mime?: string) => {
|
||||||
if (!note || (type === currentNoteType && mime === currentNoteMime)) {
|
if (!note || (type === currentNoteType && mime === currentNoteMime)) {
|
||||||
return;
|
return;
|
||||||
@ -68,71 +91,68 @@ function NoteTypeWidget({ note }: { note?: FNote | null }) {
|
|||||||
}, [ note, currentNoteType, currentNoteMime ]);
|
}, [ note, currentNoteType, currentNoteMime ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="note-type-container">
|
<>
|
||||||
<span>{t("basic_properties.note_type")}:</span>
|
{noteTypes.map(({ isNew, isBeta, type, mime, title }) => {
|
||||||
<Dropdown
|
const badges: FormListBadge[] = [];
|
||||||
dropdownContainerClassName="note-type-dropdown"
|
if (isNew) {
|
||||||
text={<span className="note-type-desc">{findTypeTitle(currentNoteType, currentNoteMime)}</span>}
|
badges.push({
|
||||||
disabled={notSelectableNoteTypes.includes(currentNoteType ?? "text")}
|
className: "new-note-type-badge",
|
||||||
>
|
text: t("note_types.new-feature")
|
||||||
{noteTypes.map(({ isNew, isBeta, type, mime, title }) => {
|
});
|
||||||
const badges: FormListBadge[] = [];
|
}
|
||||||
if (isNew) {
|
if (isBeta) {
|
||||||
badges.push({
|
badges.push({
|
||||||
className: "new-note-type-badge",
|
text: t("note_types.beta-feature")
|
||||||
text: t("note_types.new-feature")
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
if (isBeta) {
|
|
||||||
badges.push({
|
|
||||||
text: t("note_types.beta-feature")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const checked = (type === currentNoteType);
|
const checked = (type === currentNoteType);
|
||||||
if (type !== "code") {
|
if (type !== "code") {
|
||||||
return (
|
return (
|
||||||
|
<FormListItem
|
||||||
|
checked={checked}
|
||||||
|
badges={badges}
|
||||||
|
onClick={() => changeNoteType(type, mime)}
|
||||||
|
>{title}</FormListItem>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FormDropdownDivider />
|
||||||
<FormListItem
|
<FormListItem
|
||||||
checked={checked}
|
checked={checked}
|
||||||
badges={badges}
|
disabled
|
||||||
onClick={() => changeNoteType(type, mime)}
|
>
|
||||||
>{title}</FormListItem>
|
<strong>{title}</strong>
|
||||||
);
|
</FormListItem>
|
||||||
} else {
|
</>
|
||||||
return (
|
);
|
||||||
<>
|
}
|
||||||
<FormDropdownDivider />
|
})}
|
||||||
<FormListItem
|
|
||||||
checked={checked}
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<strong>{title}</strong>
|
|
||||||
</FormListItem>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
|
|
||||||
{mimeTypes.map(({ title, mime }) => (
|
{mimeTypes.map(({ title, mime }) => (
|
||||||
<FormListItem onClick={() => changeNoteType("code", mime)}>
|
<FormListItem onClick={() => changeNoteType("code", mime)}>
|
||||||
{title}
|
{title}
|
||||||
</FormListItem>
|
</FormListItem>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<FormDropdownDivider />
|
<FormDropdownDivider />
|
||||||
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
|
<FormListItem icon="bx bx-cog" onClick={() => setModalShown(true)}>{t("basic_properties.configure_code_notes")}</FormListItem>
|
||||||
</Dropdown>
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
<Modal
|
function NoteTypeOptionsModal({ modalShown, setModalShown }: { modalShown: boolean, setModalShown: (shown: boolean) => void }) {
|
||||||
className="code-mime-types-modal"
|
return (
|
||||||
title={t("code_mime_types.title")}
|
<Modal
|
||||||
show={modalShown} onHidden={() => setModalShown(false)}
|
className="code-mime-types-modal"
|
||||||
size="xl" scrollable
|
title={t("code_mime_types.title")}
|
||||||
>
|
show={modalShown} onHidden={() => setModalShown(false)}
|
||||||
<CodeMimeTypesList />
|
size="xl" scrollable
|
||||||
</Modal>
|
>
|
||||||
</div>
|
<CodeMimeTypesList />
|
||||||
)
|
</Modal>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
|
function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
|
||||||
@ -187,22 +207,11 @@ function EditabilitySelect({ note }: { note?: FNote | null }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BookmarkSwitch({ note }: { note?: FNote | null }) {
|
function BookmarkSwitch({ note }: { note?: FNote | null }) {
|
||||||
const [ isBookmarked, setIsBookmarked ] = useState<boolean>(false);
|
const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note);
|
||||||
const refreshState = useCallback(() => {
|
|
||||||
const isBookmarked = note && !!note.getParentBranches().find((b) => b.parentNoteId === "_lbBookmarks");
|
|
||||||
setIsBookmarked(!!isBookmarked);
|
|
||||||
}, [ note ]);
|
|
||||||
|
|
||||||
useEffect(() => refreshState(), [ note ]);
|
|
||||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
|
||||||
if (note && loadResults.getBranchRows().find((b) => b.noteId === note.noteId)) {
|
|
||||||
refreshState();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bookmark-switch-container">
|
<div className="bookmark-switch-container">
|
||||||
@ -210,18 +219,36 @@ function BookmarkSwitch({ note }: { note?: FNote | null }) {
|
|||||||
switchOnName={t("bookmark_switch.bookmark")} switchOnTooltip={t("bookmark_switch.bookmark_this_note")}
|
switchOnName={t("bookmark_switch.bookmark")} switchOnTooltip={t("bookmark_switch.bookmark_this_note")}
|
||||||
switchOffName={t("bookmark_switch.bookmark")} switchOffTooltip={t("bookmark_switch.remove_bookmark")}
|
switchOffName={t("bookmark_switch.bookmark")} switchOffTooltip={t("bookmark_switch.remove_bookmark")}
|
||||||
currentValue={isBookmarked}
|
currentValue={isBookmarked}
|
||||||
onChange={async (shouldBookmark) => {
|
onChange={setIsBookmarked}
|
||||||
if (!note) return;
|
|
||||||
const resp = await server.put<ToggleInParentResponse>(`notes/${note.noteId}/toggle-in-parent/_lbBookmarks/${shouldBookmark}`);
|
|
||||||
|
|
||||||
if (!resp.success && "message" in resp) {
|
|
||||||
toast.showError(resp.message);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={["root", "_hidden"].includes(note?.noteId ?? "")}
|
disabled={["root", "_hidden"].includes(note?.noteId ?? "")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNoteBookmarkState(note: FNote | null | undefined) {
|
||||||
|
const [ isBookmarked, setIsBookmarked ] = useState<boolean>(false);
|
||||||
|
const refreshState = useCallback(() => {
|
||||||
|
const isBookmarked = note && !!note.getParentBranches().find((b) => b.parentNoteId === "_lbBookmarks");
|
||||||
|
setIsBookmarked(!!isBookmarked);
|
||||||
|
}, [ note ]);
|
||||||
|
|
||||||
|
const changeHandler = useCallback(async (shouldBookmark: boolean) => {
|
||||||
|
if (!note) return;
|
||||||
|
const resp = await server.put<ToggleInParentResponse>(`notes/${note.noteId}/toggle-in-parent/_lbBookmarks/${shouldBookmark}`);
|
||||||
|
|
||||||
|
if (!resp.success && "message" in resp) {
|
||||||
|
toast.showError(resp.message);
|
||||||
|
}
|
||||||
|
}, [ note ]);
|
||||||
|
|
||||||
|
useEffect(() => refreshState(), [ refreshState ]);
|
||||||
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
|
if (note && loadResults.getBranchRows().find((b) => b.noteId === note.noteId)) {
|
||||||
|
refreshState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [ isBookmarked, changeHandler ] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
function TemplateSwitch({ note }: { note?: FNote | null }) {
|
function TemplateSwitch({ note }: { note?: FNote | null }) {
|
||||||
@ -237,16 +264,33 @@ function TemplateSwitch({ note }: { note?: FNote | null }) {
|
|||||||
currentValue={isTemplate} onChange={setIsTemplate}
|
currentValue={isTemplate} onChange={setIsTemplate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SharedSwitch({ note }: { note?: FNote | null }) {
|
function SharedSwitch({ note }: { note?: FNote | null }) {
|
||||||
|
const [ isShared, switchShareState ] = useShareState(note);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="shared-switch-container">
|
||||||
|
<FormToggle
|
||||||
|
currentValue={isShared}
|
||||||
|
onChange={switchShareState}
|
||||||
|
switchOnName={t("shared_switch.shared")} switchOnTooltip={t("shared_switch.toggle-on-title")}
|
||||||
|
switchOffName={t("shared_switch.shared")} switchOffTooltip={t("shared_switch.toggle-off-title")}
|
||||||
|
helpPage="R9pX4DGra2Vt"
|
||||||
|
disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useShareState(note: FNote | null | undefined) {
|
||||||
const [ isShared, setIsShared ] = useState(false);
|
const [ isShared, setIsShared ] = useState(false);
|
||||||
const refreshState = useCallback(() => {
|
const refreshState = useCallback(() => {
|
||||||
setIsShared(!!note?.hasAncestor("_share"));
|
setIsShared(!!note?.hasAncestor("_share"));
|
||||||
}, [ note ]);
|
}, [ note ]);
|
||||||
|
|
||||||
useEffect(() => refreshState(), [ note ]);
|
useEffect(() => refreshState(), [ refreshState ]);
|
||||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
if (note && loadResults.getBranchRows().find((b) => b.noteId === note.noteId)) {
|
if (note && loadResults.getBranchRows().find((b) => b.noteId === note.noteId)) {
|
||||||
refreshState();
|
refreshState();
|
||||||
@ -271,28 +315,29 @@ function SharedSwitch({ note }: { note?: FNote | null }) {
|
|||||||
sync.syncNow(true);
|
sync.syncNow(true);
|
||||||
}, [ note ]);
|
}, [ note ]);
|
||||||
|
|
||||||
return (
|
return [ isShared, switchShareState ] as const;
|
||||||
<div className="shared-switch-container">
|
|
||||||
<FormToggle
|
|
||||||
currentValue={isShared}
|
|
||||||
onChange={switchShareState}
|
|
||||||
switchOnName={t("shared_switch.shared")} switchOnTooltip={t("shared_switch.toggle-on-title")}
|
|
||||||
switchOffName={t("shared_switch.shared")} switchOffTooltip={t("shared_switch.toggle-off-title")}
|
|
||||||
helpPage="R9pX4DGra2Vt"
|
|
||||||
disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
|
function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
|
||||||
|
return (
|
||||||
|
<div className="note-language-container">
|
||||||
|
<span>{t("basic_properties.language")}:</span>
|
||||||
|
|
||||||
|
|
||||||
|
<NoteLanguageSelector note={note} />
|
||||||
|
<HelpButton helpPage="veGu4faJErEM" style={{ marginInlineStart: "4px" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NoteLanguageSelector({ note, extraChildren }: { note: FNote | null | undefined, extraChildren?: ComponentChildren }) {
|
||||||
|
const [ modalShown, setModalShown ] = useState(false);
|
||||||
const [ languages ] = useTriliumOption("languages");
|
const [ languages ] = useTriliumOption("languages");
|
||||||
const DEFAULT_LOCALE = {
|
const DEFAULT_LOCALE = {
|
||||||
id: "",
|
id: "",
|
||||||
name: t("note_language.not_set")
|
name: t("note_language.not_set")
|
||||||
};
|
};
|
||||||
const [ currentNoteLanguage, setCurrentNoteLanguage ] = useNoteLabel(note, "language");
|
const [ currentNoteLanguage, setCurrentNoteLanguage ] = useNoteLabel(note, "language");
|
||||||
const [ modalShown, setModalShown ] = useState(false);
|
|
||||||
const locales = useMemo(() => {
|
const locales = useMemo(() => {
|
||||||
const enabledLanguages = JSON.parse(languages ?? "[]") as string[];
|
const enabledLanguages = JSON.parse(languages ?? "[]") as string[];
|
||||||
const filteredLanguages = getAvailableLocales().filter((l) => typeof l !== "object" || enabledLanguages.includes(l.id));
|
const filteredLanguages = getAvailableLocales().filter((l) => typeof l !== "object" || enabledLanguages.includes(l.id));
|
||||||
@ -300,34 +345,37 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
|
|||||||
}, [ languages ]);
|
}, [ languages ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="note-language-container">
|
<>
|
||||||
<span>{t("basic_properties.language")}:</span>
|
|
||||||
|
|
||||||
<LocaleSelector
|
<LocaleSelector
|
||||||
locales={locales}
|
locales={locales}
|
||||||
defaultLocale={DEFAULT_LOCALE}
|
defaultLocale={DEFAULT_LOCALE}
|
||||||
currentValue={currentNoteLanguage ?? ""} onChange={setCurrentNoteLanguage}
|
currentValue={currentNoteLanguage ?? ""} onChange={setCurrentNoteLanguage}
|
||||||
extraChildren={(
|
extraChildren={<>
|
||||||
|
{extraChildren}
|
||||||
<FormListItem
|
<FormListItem
|
||||||
onClick={() => setModalShown(true)}
|
onClick={() => setModalShown(true)}
|
||||||
icon="bx bx-cog"
|
icon="bx bx-cog"
|
||||||
>{t("note_language.configure-languages")}</FormListItem>
|
>{t("note_language.configure-languages")}</FormListItem>
|
||||||
)}
|
</>}
|
||||||
>
|
/>
|
||||||
|
{createPortal(
|
||||||
|
<ContentLanguagesModal modalShown={modalShown} setModalShown={setModalShown} />,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
</LocaleSelector>
|
function ContentLanguagesModal({ modalShown, setModalShown }: { modalShown: boolean, setModalShown: (shown: boolean) => void }) {
|
||||||
|
return (
|
||||||
<HelpButton helpPage="B0lcI9xz1r8K" style={{ marginInlineStart: "4px" }} />
|
<Modal
|
||||||
|
className="content-languages-modal"
|
||||||
<Modal
|
title={t("content_language.title")}
|
||||||
className="content-languages-modal"
|
show={modalShown} onHidden={() => setModalShown(false)}
|
||||||
title={t("content_language.title")}
|
size="lg" scrollable
|
||||||
show={modalShown} onHidden={() => setModalShown(false)}
|
>
|
||||||
size="lg" scrollable
|
<ContentLanguagesList />
|
||||||
>
|
</Modal>
|
||||||
<ContentLanguagesList />
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ConvertToAttachmentResponse } from "@triliumnext/commons";
|
import { ConvertToAttachmentResponse } from "@triliumnext/commons";
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext, useState } from "preact/hooks";
|
||||||
|
|
||||||
import appContext, { CommandNames } from "../../components/app_context";
|
import appContext, { CommandNames } from "../../components/app_context";
|
||||||
import NoteContext from "../../components/note_context";
|
import NoteContext from "../../components/note_context";
|
||||||
@ -13,10 +13,12 @@ import { isElectron as getIsElectron, isMac as getIsMac } from "../../services/u
|
|||||||
import ws from "../../services/ws";
|
import ws from "../../services/ws";
|
||||||
import ActionButton from "../react/ActionButton";
|
import ActionButton from "../react/ActionButton";
|
||||||
import Dropdown from "../react/Dropdown";
|
import Dropdown from "../react/Dropdown";
|
||||||
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
|
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
|
||||||
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteProperty, useTriliumOption } from "../react/hooks";
|
import { useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumOption } from "../react/hooks";
|
||||||
import { ParentComponent } from "../react/react_utils";
|
import { ParentComponent } from "../react/react_utils";
|
||||||
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
|
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
|
||||||
|
import { NoteTypeDropdownContent, useNoteBookmarkState, useShareState } from "./BasicPropertiesTab";
|
||||||
|
import protected_session from "../../services/protected_session";
|
||||||
|
|
||||||
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
|
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
|
||||||
|
|
||||||
@ -55,8 +57,10 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
|
|||||||
const isMac = getIsMac();
|
const isMac = getIsMac();
|
||||||
const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(noteType);
|
const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(noteType);
|
||||||
const isSearchOrBook = ["search", "book"].includes(noteType);
|
const isSearchOrBook = ["search", "book"].includes(noteType);
|
||||||
|
const isHelpPage = note.noteId.startsWith("_help");
|
||||||
const [syncServerHost] = useTriliumOption("syncServerHost");
|
const [syncServerHost] = useTriliumOption("syncServerHost");
|
||||||
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
|
const { isReadOnly, enableEditing } = useIsNoteReadOnly(note, noteContext);
|
||||||
|
const isNormalViewMode = noteContext?.viewScope?.viewMode === "default";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
@ -79,6 +83,11 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
|
|||||||
{isElectron && <CommandItem command="exportAsPdf" icon="bx bxs-file-pdf" disabled={!isPrintable} text={t("note_actions.print_pdf")} />}
|
{isElectron && <CommandItem command="exportAsPdf" icon="bx bxs-file-pdf" disabled={!isPrintable} text={t("note_actions.print_pdf")} />}
|
||||||
<FormDropdownDivider />
|
<FormDropdownDivider />
|
||||||
|
|
||||||
|
{isNewLayout && isNormalViewMode && !isHelpPage && <>
|
||||||
|
<NoteBasicProperties note={note} />
|
||||||
|
<FormDropdownDivider />
|
||||||
|
</>}
|
||||||
|
|
||||||
<CommandItem icon="bx bx-import" text={t("note_actions.import_files")}
|
<CommandItem icon="bx bx-import" text={t("note_actions.import_files")}
|
||||||
disabled={isInOptionsOrHelp || note.type === "search"}
|
disabled={isInOptionsOrHelp || note.type === "search"}
|
||||||
command={() => parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} />
|
command={() => parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} />
|
||||||
@ -107,11 +116,82 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
|
|||||||
<FormDropdownDivider />
|
<FormDropdownDivider />
|
||||||
|
|
||||||
<CommandItem command="showAttachments" icon="bx bx-paperclip" disabled={isInOptionsOrHelp} text={t("note_actions.note_attachments")} />
|
<CommandItem command="showAttachments" icon="bx bx-paperclip" disabled={isInOptionsOrHelp} text={t("note_actions.note_attachments")} />
|
||||||
{glob.isDev && <DevelopmentActions note={note} noteContext={noteContext} />}
|
{glob.isDev && <>
|
||||||
|
<FormDropdownDivider />
|
||||||
|
<DevelopmentActions note={note} noteContext={noteContext} />
|
||||||
|
</>}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function NoteBasicProperties({ note }: { note: FNote }) {
|
||||||
|
const [ isBookmarked, setIsBookmarked ] = useNoteBookmarkState(note);
|
||||||
|
const [ isShared, switchShareState ] = useShareState(note);
|
||||||
|
const [ isTemplate, setIsTemplate ] = useNoteLabelBoolean(note, "template");
|
||||||
|
const isProtected = useNoteProperty(note, "isProtected");
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<FormListToggleableItem
|
||||||
|
icon="bx bx-bookmark"
|
||||||
|
title={t("bookmark_switch.bookmark")}
|
||||||
|
currentValue={isBookmarked} onChange={setIsBookmarked}
|
||||||
|
disabled={["root", "_hidden"].includes(note?.noteId ?? "")}
|
||||||
|
/>
|
||||||
|
<FormListToggleableItem
|
||||||
|
icon="bx bx-copy-alt"
|
||||||
|
title={t("template_switch.template")}
|
||||||
|
currentValue={isTemplate} onChange={setIsTemplate}
|
||||||
|
helpPage="KC1HB96bqqHX"
|
||||||
|
disabled={note?.noteId.startsWith("_options")}
|
||||||
|
/>
|
||||||
|
<FormListToggleableItem
|
||||||
|
icon="bx bx-share-alt"
|
||||||
|
title={t("shared_switch.shared")}
|
||||||
|
currentValue={isShared} onChange={switchShareState}
|
||||||
|
helpPage="R9pX4DGra2Vt"
|
||||||
|
disabled={["root", "_share", "_hidden"].includes(note?.noteId ?? "") || note?.noteId.startsWith("_options")}
|
||||||
|
/>
|
||||||
|
<EditabilityDropdown note={note} />
|
||||||
|
<FormListToggleableItem
|
||||||
|
icon="bx bx-lock-alt"
|
||||||
|
title={t("protect_note.toggle-on")}
|
||||||
|
currentValue={!!isProtected} onChange={shouldProtect => protected_session.protectNote(note.noteId, shouldProtect, false)}
|
||||||
|
/>
|
||||||
|
<FormDropdownDivider />
|
||||||
|
<NoteTypeDropdown note={note} />
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EditabilityDropdown({ note }: { note: FNote }) {
|
||||||
|
const [ readOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
|
||||||
|
const [ autoReadOnlyDisabled, setAutoReadOnlyDisabled ] = useNoteLabelBoolean(note, "autoReadOnlyDisabled");
|
||||||
|
|
||||||
|
function setState(readOnly: boolean, autoReadOnlyDisabled: boolean) {
|
||||||
|
setReadOnly(readOnly);
|
||||||
|
setAutoReadOnlyDisabled(autoReadOnlyDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormDropdownSubmenu title={t("basic_properties.editable")} icon="bx bx-edit-alt" dropStart>
|
||||||
|
<FormListItem checked={!readOnly && !autoReadOnlyDisabled} onClick={() => setState(false, false)} description={t("editability_select.note_is_editable")}>{t("editability_select.auto")}</FormListItem>
|
||||||
|
<FormListItem checked={readOnly && !autoReadOnlyDisabled} onClick={() => setState(true, false)} description={t("editability_select.note_is_read_only")}>{t("editability_select.read_only")}</FormListItem>
|
||||||
|
<FormListItem checked={!readOnly && autoReadOnlyDisabled} onClick={() => setState(false, true)} description={t("editability_select.note_is_always_editable")}>{t("editability_select.always_editable")}</FormListItem>
|
||||||
|
</FormDropdownSubmenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function NoteTypeDropdown({ note }: { note: FNote }) {
|
||||||
|
const currentNoteType = useNoteProperty(note, "type") ?? undefined;
|
||||||
|
const currentNoteMime = useNoteProperty(note, "mime");
|
||||||
|
const [ modalShown, setModalShown ] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormDropdownSubmenu title={t("basic_properties.note_type")} icon="bx bx-file" dropStart>
|
||||||
|
<NoteTypeDropdownContent currentNoteType={currentNoteType} currentNoteMime={currentNoteMime} note={note} setModalShown={setModalShown} />
|
||||||
|
</FormDropdownSubmenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function DevelopmentActions({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
|
function DevelopmentActions({ note, noteContext }: { note: FNote, noteContext?: NoteContext }) {
|
||||||
return (
|
return (
|
||||||
<FormDropdownSubmenu title="Development Actions" icon="bx bx-wrench" dropStart>
|
<FormDropdownSubmenu title="Development Actions" icon="bx bx-wrench" dropStart>
|
||||||
|
|||||||
@ -1,22 +1,24 @@
|
|||||||
import ScriptTab from "./ScriptTab";
|
|
||||||
import EditedNotesTab from "./EditedNotesTab";
|
|
||||||
import NotePropertiesTab from "./NotePropertiesTab";
|
|
||||||
import NoteInfoTab from "./NoteInfoTab";
|
|
||||||
import SimilarNotesTab from "./SimilarNotesTab";
|
|
||||||
import FilePropertiesTab from "./FilePropertiesTab";
|
|
||||||
import ImagePropertiesTab from "./ImagePropertiesTab";
|
|
||||||
import NotePathsTab from "./NotePathsTab";
|
|
||||||
import NoteMapTab from "./NoteMapTab";
|
|
||||||
import OwnedAttributesTab from "./OwnedAttributesTab";
|
|
||||||
import InheritedAttributesTab from "./InheritedAttributesTab";
|
|
||||||
import CollectionPropertiesTab from "./CollectionPropertiesTab";
|
|
||||||
import SearchDefinitionTab from "./SearchDefinitionTab";
|
|
||||||
import BasicPropertiesTab from "./BasicPropertiesTab";
|
|
||||||
import FormattingToolbar from "./FormattingToolbar";
|
|
||||||
import options from "../../services/options";
|
|
||||||
import { t } from "../../services/i18n";
|
|
||||||
import { TabConfiguration } from "./ribbon-interface";
|
|
||||||
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
|
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
|
||||||
|
import { t } from "../../services/i18n";
|
||||||
|
import options from "../../services/options";
|
||||||
|
import BasicPropertiesTab from "./BasicPropertiesTab";
|
||||||
|
import CollectionPropertiesTab from "./CollectionPropertiesTab";
|
||||||
|
import EditedNotesTab from "./EditedNotesTab";
|
||||||
|
import FilePropertiesTab from "./FilePropertiesTab";
|
||||||
|
import FormattingToolbar from "./FormattingToolbar";
|
||||||
|
import ImagePropertiesTab from "./ImagePropertiesTab";
|
||||||
|
import InheritedAttributesTab from "./InheritedAttributesTab";
|
||||||
|
import NoteInfoTab from "./NoteInfoTab";
|
||||||
|
import NoteMapTab from "./NoteMapTab";
|
||||||
|
import NotePathsTab from "./NotePathsTab";
|
||||||
|
import NotePropertiesTab from "./NotePropertiesTab";
|
||||||
|
import OwnedAttributesTab from "./OwnedAttributesTab";
|
||||||
|
import { TabConfiguration } from "./ribbon-interface";
|
||||||
|
import ScriptTab from "./ScriptTab";
|
||||||
|
import SearchDefinitionTab from "./SearchDefinitionTab";
|
||||||
|
import SimilarNotesTab from "./SimilarNotesTab";
|
||||||
|
|
||||||
|
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
|
||||||
|
|
||||||
export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
||||||
{
|
{
|
||||||
@ -28,7 +30,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
|||||||
toggleCommand: "toggleRibbonTabClassicEditor",
|
toggleCommand: "toggleRibbonTabClassicEditor",
|
||||||
content: FormattingToolbar,
|
content: FormattingToolbar,
|
||||||
activate: ({ note }) => !options.is("editedNotesOpenInRibbon") || !note?.hasOwnedLabel("dateNote"),
|
activate: ({ note }) => !options.is("editedNotesOpenInRibbon") || !note?.hasOwnedLabel("dateNote"),
|
||||||
stayInDom: !isExperimentalFeatureEnabled("new-layout"),
|
stayInDom: !isNewLayout,
|
||||||
avoidInNewLayout: true
|
avoidInNewLayout: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -85,11 +87,10 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
|||||||
activate: true,
|
activate: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// BasicProperties
|
|
||||||
title: t("basic_properties.basic_properties"),
|
title: t("basic_properties.basic_properties"),
|
||||||
icon: "bx bx-slider",
|
icon: "bx bx-slider",
|
||||||
content: BasicPropertiesTab,
|
content: BasicPropertiesTab,
|
||||||
show: ({note}) => !note?.isLaunchBarConfig(),
|
show: ({note}) => !isNewLayout && !note?.isLaunchBarConfig(),
|
||||||
toggleCommand: "toggleRibbonTabBasicProperties"
|
toggleCommand: "toggleRibbonTabBasicProperties"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { Locale } from "@triliumnext/commons";
|
import { Locale } from "@triliumnext/commons";
|
||||||
|
import { ComponentChildren } from "preact";
|
||||||
|
import { useMemo } from "preact/hooks";
|
||||||
|
|
||||||
import Dropdown from "../../../react/Dropdown";
|
import Dropdown from "../../../react/Dropdown";
|
||||||
import { FormDropdownDivider, FormListItem } from "../../../react/FormList";
|
import { FormDropdownDivider, FormListItem } from "../../../react/FormList";
|
||||||
import { ComponentChildren } from "preact";
|
|
||||||
import { useMemo, useState } from "preact/hooks";
|
|
||||||
|
|
||||||
export function LocaleSelector({ id, locales, currentValue, onChange, defaultLocale, extraChildren }: {
|
export function LocaleSelector({ id, locales, currentValue, onChange, defaultLocale, extraChildren }: {
|
||||||
id?: string;
|
id?: string;
|
||||||
@ -12,7 +13,7 @@ export function LocaleSelector({ id, locales, currentValue, onChange, defaultLoc
|
|||||||
defaultLocale?: Locale,
|
defaultLocale?: Locale,
|
||||||
extraChildren?: ComponentChildren
|
extraChildren?: ComponentChildren
|
||||||
}) {
|
}) {
|
||||||
const [ activeLocale, setActiveLocale ] = useState(defaultLocale?.id === currentValue ? defaultLocale : locales.find(l => l.id === currentValue));
|
const activeLocale = defaultLocale?.id === currentValue ? defaultLocale : locales.find(l => l.id === currentValue);
|
||||||
|
|
||||||
const processedLocales = useMemo(() => {
|
const processedLocales = useMemo(() => {
|
||||||
const leftToRightLanguages = locales.filter((l) => !l.rtl);
|
const leftToRightLanguages = locales.filter((l) => !l.rtl);
|
||||||
@ -48,7 +49,6 @@ export function LocaleSelector({ id, locales, currentValue, onChange, defaultLoc
|
|||||||
rtl={locale.rtl}
|
rtl={locale.rtl}
|
||||||
checked={locale.id === currentValue}
|
checked={locale.id === currentValue}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveLocale(locale);
|
|
||||||
onChange(locale.id);
|
onChange(locale.id);
|
||||||
}}
|
}}
|
||||||
>{locale.name}</FormListItem>
|
>{locale.name}</FormListItem>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user