mirror of
https://github.com/zadam/trilium.git
synced 2026-04-11 14:37:41 +02:00
refactor(delete): deduplicate form toggle
This commit is contained in:
@@ -3,7 +3,7 @@ import "./delete_notes.css";
|
||||
import { useRef, useState, useEffect } from "preact/hooks";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import Modal from "../react/Modal.js";
|
||||
import Toggle from "../react/Toggle.js";
|
||||
import FormToggle from "../react/FormToggle.js";
|
||||
import type { DeleteNotesPreview } from "@triliumnext/commons";
|
||||
import server from "../../services/server.js";
|
||||
import froca from "../../services/froca.js";
|
||||
@@ -147,7 +147,7 @@ export default function DeleteNotesDialog() {
|
||||
label={t("delete_notes.erase_notes_label")}
|
||||
description={t("delete_notes.erase_notes_description")}
|
||||
>
|
||||
<Toggle
|
||||
<FormToggle
|
||||
disabled={opts.forceDeleteAllClones}
|
||||
currentValue={eraseNotes}
|
||||
onChange={setEraseNotes}
|
||||
@@ -190,7 +190,7 @@ function DeleteAllClonesOption({ cloneInfo, deleteAllClones, setDeleteAllClones
|
||||
label={t("delete_notes.clones_label")}
|
||||
description={t("delete_notes.delete_clones_description", { count: totalCloneCount })}
|
||||
>
|
||||
<Toggle
|
||||
<FormToggle
|
||||
currentValue={deleteAllClones}
|
||||
onChange={setDeleteAllClones}
|
||||
/>
|
||||
|
||||
@@ -7,17 +7,22 @@ import { ComponentChildren } from "preact";
|
||||
interface FormToggleProps {
|
||||
currentValue: boolean | null;
|
||||
onChange(newValue: boolean): void;
|
||||
switchOnName: string;
|
||||
/** Label shown when toggle is off. If omitted along with switchOffName, no label is shown. */
|
||||
switchOnName?: string;
|
||||
switchOnTooltip?: string;
|
||||
switchOffName: string;
|
||||
/** Label shown when toggle is on. If omitted along with switchOnName, no label is shown. */
|
||||
switchOffName?: string;
|
||||
switchOffTooltip?: string;
|
||||
helpPage?: string;
|
||||
disabled?: boolean;
|
||||
afterName?: ComponentChildren;
|
||||
/** ID for the input element, useful for accessibility with external labels */
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export default function FormToggle({ currentValue, helpPage, switchOnName, switchOnTooltip, switchOffName, switchOffTooltip, onChange, disabled, afterName }: FormToggleProps) {
|
||||
export default function FormToggle({ currentValue, helpPage, switchOnName, switchOnTooltip, switchOffName, switchOffTooltip, onChange, disabled, afterName, id }: FormToggleProps) {
|
||||
const [ disableTransition, setDisableTransition ] = useState(true);
|
||||
const hasLabel = switchOnName || switchOffName;
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
@@ -28,7 +33,7 @@ export default function FormToggle({ currentValue, helpPage, switchOnName, switc
|
||||
|
||||
return (
|
||||
<div className="switch-widget">
|
||||
<span className="switch-name">{ currentValue ? switchOffName : switchOnName }</span>
|
||||
{hasLabel && <span className="switch-name">{ currentValue ? switchOffName : switchOnName }</span>}
|
||||
{ afterName }
|
||||
|
||||
<label>
|
||||
@@ -37,6 +42,7 @@ export default function FormToggle({ currentValue, helpPage, switchOnName, switc
|
||||
title={currentValue ? switchOffTooltip : switchOnTooltip }
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
className="switch-toggle"
|
||||
type="checkbox"
|
||||
checked={currentValue === true}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
.tn-toggle {
|
||||
--toggle-track-width: 44px;
|
||||
--toggle-track-height: 24px;
|
||||
--toggle-off-track-background: var(--more-accented-background-color);
|
||||
--toggle-on-track-background: var(--main-text-color);
|
||||
|
||||
--toggle-thumb-size: 18px;
|
||||
--toggle-thumb-background: var(--main-background-color);
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* The track of the toggle switch */
|
||||
.tn-toggle-track {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: var(--toggle-track-width);
|
||||
height: var(--toggle-track-height);
|
||||
border-radius: var(--toggle-track-height);
|
||||
background-color: var(--toggle-off-track-background);
|
||||
transition: background 200ms ease-in;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tn-toggle-track.disable-transitions,
|
||||
.tn-toggle-track.disable-transitions::after {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.tn-toggle-track.on {
|
||||
background: var(--toggle-on-track-background);
|
||||
transition: background 100ms ease-out;
|
||||
}
|
||||
|
||||
/* The thumb of the toggle switch */
|
||||
.tn-toggle-track::after {
|
||||
--offset: calc((var(--toggle-track-height) - var(--toggle-thumb-size)) / 2);
|
||||
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: var(--offset);
|
||||
inset-inline-start: var(--offset);
|
||||
width: var(--toggle-thumb-size);
|
||||
height: var(--toggle-thumb-size);
|
||||
background-color: var(--toggle-thumb-background);
|
||||
border-radius: 50%;
|
||||
transition: transform 600ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.tn-toggle-track.on::after {
|
||||
transform: translateX(calc(var(--toggle-track-width) - var(--toggle-thumb-size) - var(--offset) * 2));
|
||||
transition: transform 200ms cubic-bezier(0.64, 0, 0.78, 0);
|
||||
}
|
||||
|
||||
body[dir="rtl"] .tn-toggle-track.on::after {
|
||||
transform: translateX(calc((var(--toggle-track-width) - var(--toggle-thumb-size) - var(--offset) * 2) * -1));
|
||||
}
|
||||
|
||||
/* Hidden checkbox for accessibility */
|
||||
.tn-toggle-input {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Focus state */
|
||||
.tn-toggle-track:has(.tn-toggle-input:focus-visible) {
|
||||
outline: 2px solid var(--button-border-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Disabled state */
|
||||
.tn-toggle-track.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tn-toggle-track.disabled .tn-toggle-input {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import "./Toggle.css";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
interface ToggleProps {
|
||||
currentValue: boolean;
|
||||
onChange(newValue: boolean): void;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple toggle switch without labels. For use with OptionsRow or other
|
||||
* contexts where the label is provided separately.
|
||||
*/
|
||||
export default function Toggle({ currentValue, onChange, disabled, id }: ToggleProps) {
|
||||
const [disableTransition, setDisableTransition] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
setDisableTransition(false);
|
||||
}, 100);
|
||||
return () => clearTimeout(timeout);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<label className="tn-toggle">
|
||||
<div
|
||||
className={clsx("tn-toggle-track", {
|
||||
on: currentValue,
|
||||
disabled,
|
||||
"disable-transitions": disableTransition
|
||||
})}
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
className="tn-toggle-input"
|
||||
type="checkbox"
|
||||
checked={currentValue}
|
||||
onInput={(e) => {
|
||||
onChange(!currentValue);
|
||||
e.preventDefault();
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user