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. */