diff --git a/apps/client/src/widgets/protected_note_switch.ts b/apps/client/src/widgets/protected_note_switch.ts
deleted file mode 100644
index a65f4db31..000000000
--- a/apps/client/src/widgets/protected_note_switch.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import type { EventData } from "../components/app_context.js";
-import type FNote from "../entities/fnote.js";
-import { t } from "../services/i18n.js";
-import protectedSessionService from "../services/protected_session.js";
-import SwitchWidget from "./switch.js";
-
-export default class ProtectedNoteSwitchWidget extends SwitchWidget {
- doRender() {
- super.doRender();
-
- this.switchOnName = t("protect_note.toggle-on");
- this.switchOnTooltip = t("protect_note.toggle-on-hint");
-
- this.switchOffName = t("protect_note.toggle-off");
- this.switchOffTooltip = t("protect_note.toggle-off-hint");
- }
-
- switchOn() {
- if (this.noteId) {
- protectedSessionService.protectNote(this.noteId, true, false);
- }
- }
-
- switchOff() {
- if (this.noteId) {
- protectedSessionService.protectNote(this.noteId, false, false);
- }
- }
-
- async refreshWithNote(note: FNote) {
- this.isToggled = note.isProtected;
- }
-
- entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
- if (loadResults.isNoteReloaded(this.noteId)) {
- this.refresh();
- }
- }
-}
diff --git a/apps/client/src/widgets/react/FormToggle.css b/apps/client/src/widgets/react/FormToggle.css
new file mode 100644
index 000000000..f727bc5ff
--- /dev/null
+++ b/apps/client/src/widgets/react/FormToggle.css
@@ -0,0 +1,98 @@
+.switch-widget {
+ --switch-track-width: 50px;
+ --switch-track-height: 24px;
+ --switch-off-track-background: var(--more-accented-background-color);
+ --switch-on-track-background: var(--main-text-color);
+
+ --switch-thumb-width: 16px;
+ --switch-thumb-height: 16px;
+ --switch-off-thumb-background: var(--main-background-color);
+ --switch-on-thumb-background: var(--main-background-color);
+
+ display: flex;
+ align-items: center;
+}
+
+/* The track of the toggle switch */
+
+.switch-widget .switch-button {
+ display: block;
+ position: relative;
+ margin-left: 8px;
+ width: var(--switch-track-width);
+ height: var(--switch-track-height);
+ border-radius: 24px;
+ background-color: var(--switch-off-track-background);
+ transition: background 200ms ease-in;
+}
+
+.switch-widget .switch-button.on {
+ background: var(--switch-on-track-background);
+ transition: background 100ms ease-out;
+}
+
+/* The thumb of the toggle switch */
+
+.switch-widget .switch-button:after {
+ --y: calc((var(--switch-track-height) - var(--switch-thumb-height)) / 2);
+ --x: var(--y);
+
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: var(--switch-thumb-width);
+ height: var(--switch-thumb-height);
+ background-color: var(--switch-off-thumb-background);
+ border-radius: 50%;
+ transform: translate(var(--x), var(--y));
+ transition: transform 600ms cubic-bezier(0.22, 1, 0.36, 1),
+ background 200ms ease-out;
+}
+
+.switch-widget .switch-button.on:after {
+ --x: calc(var(--switch-track-width) - var(--switch-thumb-width) - var(--y));
+
+ background: var(--switch-on-thumb-background);
+ transition: transform 200ms cubic-bezier(0.64, 0, 0.78, 0),
+ background 100ms ease-in;
+}
+
+
+.switch-widget .switch-button input[type="checkbox"] {
+ /* A hidden check box for accesibility purposes */
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+}
+
+/* Disabled state */
+.switch-widget .switch-button:not(.disabled) input[type="checkbox"],
+.switch-widget .switch-button:not(.disabled) {
+ cursor: pointer;
+}
+
+.switch-widget .switch-button:has(input[type="checkbox"]:focus-visible) {
+ outline: 2px solid var(--button-border-color);
+ outline-offset: 2px;
+}
+
+.switch-widget .switch-button.disabled {
+ opacity: 70%;
+}
+
+.switch-widget .switch-help-button {
+ border: 0;
+ margin-left: 4px;
+ background: none;
+ cursor: pointer;
+ font-size: 1.1em;
+ color: var(--muted-text-color);
+}
+
+.switch-widget .switch-help-button:hover {
+ color: var(--main-text-color);
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/react/FormToggle.tsx b/apps/client/src/widgets/react/FormToggle.tsx
new file mode 100644
index 000000000..a6dc2fe1d
--- /dev/null
+++ b/apps/client/src/widgets/react/FormToggle.tsx
@@ -0,0 +1,47 @@
+import { t } from "../../services/i18n";
+import { openInAppHelpFromUrl } from "../../services/utils";
+import "./FormToggle.css";
+
+interface FormToggleProps {
+ currentValue: boolean | null;
+ onChange(newValue: boolean): void;
+ switchOnName: string;
+ switchOnTooltip: string;
+ switchOffName: string;
+ switchOffTooltip: string;
+ helpPage?: string;
+}
+
+export default function FormToggle({ currentValue, helpPage, switchOnName, switchOnTooltip, switchOffName, switchOffTooltip, onChange }: FormToggleProps) {
+ return (
+
+
{ currentValue ? switchOffName : switchOnName }
+
+
+
+ { helpPage && (
+
+ )
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx
index fe10d1fde..5c7ab1937 100644
--- a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx
+++ b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx
@@ -8,22 +8,27 @@ import mime_types from "../../services/mime_types";
import { NoteType } from "@triliumnext/commons";
import server from "../../services/server";
import dialog from "../../services/dialog";
+import FormToggle from "../react/FormToggle";
+import FNote from "../../entities/fnote";
+import protected_session from "../../services/protected_session";
export default function BasicPropertiesTab() {
+ const { note } = useNoteContext();
+
return (
-
-
+
);
}
-function NoteTypeWidget() {
+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.getMimeTypes().filter(mimeType => mimeType.enabled), [ codeNotesMimeTypes ]);
const notSelectableNoteTypes = useMemo(() => NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type), []);
- const { note } = useNoteContext();
const currentNoteType = useNoteProperty(note, "type") ?? undefined;
const currentNoteMime = useNoteProperty(note, "mime");
@@ -101,6 +106,21 @@ function NoteTypeWidget() {
)
}
+function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
+ const isProtected = useNoteProperty(note, "isProtected");
+
+ return (
+
+ note && protected_session.protectNote(note.noteId, shouldProtect, false)}
+ switchOnName={t("protect_note.toggle-on")} switchOnTooltip={t("protect_note.toggle-on-hint")}
+ switchOffName={t("protect_note.toggle-off")} switchOffTooltip={t("protect_note.toggle-off-hint")}
+ />
+
+ )
+}
+
function findTypeTitle(type?: NoteType, mime?: string | null) {
if (type === "code") {
const mimeTypes = mime_types.getMimeTypes();
diff --git a/apps/client/src/widgets/ribbon_widgets/basic_properties.ts b/apps/client/src/widgets/ribbon_widgets/basic_properties.ts
index 1a59f3d48..14fb7468f 100644
--- a/apps/client/src/widgets/ribbon_widgets/basic_properties.ts
+++ b/apps/client/src/widgets/ribbon_widgets/basic_properties.ts
@@ -10,8 +10,6 @@ import type FNote from "../../entities/fnote.js";
import NoteLanguageWidget from "../note_language.js";
const TPL = /*html*/`
-
-
${t("basic_properties.editable")}:
@@ -40,8 +38,6 @@ export default class BasicPropertiesWidget extends NoteContextAwareWidget {
constructor() {
super();
- this.noteTypeWidget = new NoteTypeWidget().contentSized();
- this.protectedNoteSwitchWidget = new ProtectedNoteSwitchWidget().contentSized();
this.editabilitySelectWidget = new EditabilitySelectWidget().contentSized();
this.bookmarkSwitchWidget = new BookmarkSwitchWidget().contentSized();
this.sharedSwitchWidget = new SharedSwitchWidget().contentSized();
diff --git a/apps/client/src/widgets/switch.ts b/apps/client/src/widgets/switch.ts
index 166165fc1..8c521fb99 100644
--- a/apps/client/src/widgets/switch.ts
+++ b/apps/client/src/widgets/switch.ts
@@ -4,162 +4,22 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
const TPL = /*html*/`
`;
export default class SwitchWidget extends NoteContextAwareWidget {
- private $switchButton!: JQuery
;
- private $switchToggle!: JQuery;
- private $switchName!: JQuery;
- protected $helpButton!: JQuery;
-
- protected switchOnName = "";
- protected switchOnTooltip = "";
-
- protected switchOffName = "";
- protected switchOffTooltip = "";
-
- protected disabledTooltip = "";
-
- private currentState = false;
-
doRender() {
this.$widget = $(TPL);
this.$switchButton = this.$widget.find(".switch-button");
this.$switchToggle = this.$widget.find(".switch-toggle");
- this.$switchToggle.on("click", (e) => {
- this.toggle(!this.currentState);
-
- // Prevent the check box from being toggled by the click, the value of the check box
- // should be set exclusively by the 'isToggled' property setter.
- e.preventDefault();
- });
-
this.$switchName = this.$widget.find(".switch-name");
this.$helpButton = this.$widget.find(".switch-help-button");
}
- toggle(state: boolean) {
- if (state) {
- this.switchOn();
- } else {
- this.switchOff();
- }
- }
-
switchOff() {}
switchOn() {}
@@ -172,15 +32,6 @@ export default class SwitchWidget extends NoteContextAwareWidget {
this.currentState = !!state;
this.$switchButton.toggleClass("on", this.currentState);
- this.$switchToggle.prop("checked", this.currentState);
-
- if (this.currentState) {
- this.$switchName.text(this.switchOffName);
- this.$switchButton.attr("title", this.switchOffTooltip);
- } else {
- this.$switchName.text(this.switchOnName);
- this.$switchButton.attr("title", this.switchOnTooltip);
- }
}
/** Gets or sets whether the switch is enabled. */