mirror of
https://github.com/zadam/trilium.git
synced 2025-10-21 07:38:53 +02:00
refactor(react/settings): associate IDs for labels
This commit is contained in:
parent
0841603be0
commit
51291a61e6
@ -144,6 +144,10 @@ textarea,
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Add a gap between consecutive radios / check boxes */
|
/* Add a gap between consecutive radios / check boxes */
|
||||||
label.tn-radio + label.tn-radio,
|
label.tn-radio + label.tn-radio,
|
||||||
label.tn-checkbox + label.tn-checkbox {
|
label.tn-checkbox + label.tn-checkbox {
|
||||||
|
@ -5,7 +5,7 @@ import { ComponentChildren } from "preact";
|
|||||||
import { CSSProperties, memo } from "preact/compat";
|
import { CSSProperties, memo } from "preact/compat";
|
||||||
|
|
||||||
interface FormCheckboxProps {
|
interface FormCheckboxProps {
|
||||||
name: string;
|
id?: string;
|
||||||
label: string | ComponentChildren;
|
label: string | ComponentChildren;
|
||||||
/**
|
/**
|
||||||
* If set, the checkbox label will be underlined and dotted, indicating a hint. When hovered, it will show the hint text.
|
* If set, the checkbox label will be underlined and dotted, indicating a hint. When hovered, it will show the hint text.
|
||||||
@ -17,7 +17,7 @@ interface FormCheckboxProps {
|
|||||||
containerStyle?: CSSProperties;
|
containerStyle?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) => {
|
const FormCheckbox = memo(({ id, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) => {
|
||||||
const labelRef = useRef<HTMLLabelElement>(null);
|
const labelRef = useRef<HTMLLabelElement>(null);
|
||||||
|
|
||||||
// Fix: Move useEffect outside conditional
|
// Fix: Move useEffect outside conditional
|
||||||
@ -55,9 +55,10 @@ const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint
|
|||||||
ref={labelRef}
|
ref={labelRef}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
id={id}
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name={name}
|
name={id}
|
||||||
checked={currentValue || false}
|
checked={currentValue || false}
|
||||||
value="1"
|
value="1"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
@ -1,25 +1,29 @@
|
|||||||
import { ComponentChildren, RefObject } from "preact";
|
import { cloneElement, ComponentChildren, RefObject, VNode } from "preact";
|
||||||
|
import { CSSProperties } from "preact/compat";
|
||||||
|
import { useUniqueName } from "./hooks";
|
||||||
|
|
||||||
interface FormGroupProps {
|
interface FormGroupProps {
|
||||||
|
name: string;
|
||||||
labelRef?: RefObject<HTMLLabelElement>;
|
labelRef?: RefObject<HTMLLabelElement>;
|
||||||
label?: string;
|
label?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
children: ComponentChildren;
|
children: VNode<any>;
|
||||||
description?: string | ComponentChildren;
|
description?: string | ComponentChildren;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FormGroup({ label, title, className, children, description, labelRef, disabled }: FormGroupProps) {
|
export default function FormGroup({ name, label, title, className, children, description, labelRef, disabled, style }: FormGroupProps) {
|
||||||
|
const id = useUniqueName(name);
|
||||||
|
const childWithId = cloneElement(children, { id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`form-group ${className} ${disabled ? "disabled" : ""}`} title={title}
|
<div className={`form-group ${className} ${disabled ? "disabled" : ""}`} title={title} style={style}>
|
||||||
style={{ "margin-bottom": "15px" }}>
|
{ label &&
|
||||||
{ label
|
<label style={{ width: "100%" }} ref={labelRef} htmlFor={id}>{label}</label>}
|
||||||
? <label style={{ width: "100%" }} ref={labelRef}>
|
|
||||||
{label && <div style={{ "margin-bottom": "10px" }}>{label}</div> }
|
{childWithId}
|
||||||
{children}
|
|
||||||
</label>
|
|
||||||
: children}
|
|
||||||
|
|
||||||
{description && <small className="form-text">{description}</small>}
|
{description && <small className="form-text">{description}</small>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,6 +19,7 @@ interface ValueConfig<T, Q> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface FormSelectProps<T, Q> extends ValueConfig<T, Q> {
|
interface FormSelectProps<T, Q> extends ValueConfig<T, Q> {
|
||||||
|
id?: string;
|
||||||
onChange: OnChangeListener;
|
onChange: OnChangeListener;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
@ -26,9 +27,9 @@ interface FormSelectProps<T, Q> extends ValueConfig<T, Q> {
|
|||||||
/**
|
/**
|
||||||
* Combobox component that takes in any object array as data. Each item of the array is rendered as an item, and the key and values are obtained by looking into the object by a specified key.
|
* Combobox component that takes in any object array as data. Each item of the array is rendered as an item, and the key and values are obtained by looking into the object by a specified key.
|
||||||
*/
|
*/
|
||||||
export default function FormSelect<T>({ onChange, style, ...restProps }: FormSelectProps<T, T>) {
|
export default function FormSelect<T>({ id, onChange, style, ...restProps }: FormSelectProps<T, T>) {
|
||||||
return (
|
return (
|
||||||
<FormSelectBody onChange={onChange} style={style}>
|
<FormSelectBody id={id} onChange={onChange} style={style}>
|
||||||
<FormSelectGroup {...restProps} />
|
<FormSelectGroup {...restProps} />
|
||||||
</FormSelectBody>
|
</FormSelectBody>
|
||||||
);
|
);
|
||||||
@ -37,9 +38,9 @@ export default function FormSelect<T>({ onChange, style, ...restProps }: FormSel
|
|||||||
/**
|
/**
|
||||||
* Similar to {@link FormSelect}, but the top-level elements are actually groups.
|
* Similar to {@link FormSelect}, but the top-level elements are actually groups.
|
||||||
*/
|
*/
|
||||||
export function FormSelectWithGroups<T>({ values, keyProperty, titleProperty, currentValue, onChange }: FormSelectProps<T, FormSelectGroup<T>>) {
|
export function FormSelectWithGroups<T>({ id, values, keyProperty, titleProperty, currentValue, onChange }: FormSelectProps<T, FormSelectGroup<T>>) {
|
||||||
return (
|
return (
|
||||||
<FormSelectBody onChange={onChange}>
|
<FormSelectBody id={id} onChange={onChange}>
|
||||||
{values.map(({ title, items }) => {
|
{values.map(({ title, items }) => {
|
||||||
return (
|
return (
|
||||||
<optgroup label={title}>
|
<optgroup label={title}>
|
||||||
@ -51,9 +52,10 @@ export function FormSelectWithGroups<T>({ values, keyProperty, titleProperty, cu
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormSelectBody({ children, onChange, style }: { children: ComponentChildren, onChange: OnChangeListener, style?: CSSProperties }) {
|
function FormSelectBody({ id, children, onChange, style }: { id?: string, children: ComponentChildren, onChange: OnChangeListener, style?: CSSProperties }) {
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
|
id={id}
|
||||||
class="form-select"
|
class="form-select"
|
||||||
onChange={e => onChange((e.target as HTMLInputElement).value)}
|
onChange={e => onChange((e.target as HTMLInputElement).value)}
|
||||||
style={style}
|
style={style}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
interface FormTextAreaProps {
|
interface FormTextAreaProps {
|
||||||
|
id?: string;
|
||||||
currentValue: string;
|
currentValue: string;
|
||||||
onBlur?(newValue: string): void;
|
onBlur?(newValue: string): void;
|
||||||
rows: number;
|
rows: number;
|
||||||
}
|
}
|
||||||
export default function FormTextArea({ onBlur, rows, currentValue }: FormTextAreaProps) {
|
export default function FormTextArea({ id, onBlur, rows, currentValue }: FormTextAreaProps) {
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
|
id={id}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
onBlur?.(e.currentTarget.value);
|
onBlur?.(e.currentTarget.value);
|
||||||
|
@ -191,5 +191,5 @@ export function useTriliumOptions<T extends OptionNames>(...names: T[]) {
|
|||||||
* @returns a name with the given prefix and a random alpanumeric string appended to it.
|
* @returns a name with the given prefix and a random alpanumeric string appended to it.
|
||||||
*/
|
*/
|
||||||
export function useUniqueName(prefix: string) {
|
export function useUniqueName(prefix: string) {
|
||||||
return useMemo(() => prefix + utils.randomString(10), [ prefix]);
|
return useMemo(() => prefix + "-" + utils.randomString(10), [ prefix]);
|
||||||
}
|
}
|
@ -1,17 +1,22 @@
|
|||||||
import { ComponentChildren } from "preact";
|
import { cloneElement, VNode } from "preact";
|
||||||
import "./OptionsRow.css";
|
import "./OptionsRow.css";
|
||||||
|
import { useUniqueName } from "../../../react/hooks";
|
||||||
|
|
||||||
interface OptionsRowProps {
|
interface OptionsRowProps {
|
||||||
|
name: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
children: ComponentChildren;
|
children: VNode;
|
||||||
centered?: boolean;
|
centered?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function OptionsRow({ label, children, centered }: OptionsRowProps) {
|
export default function OptionsRow({ name, label, children, centered }: OptionsRowProps) {
|
||||||
|
const id = useUniqueName(name);
|
||||||
|
const childWithId = cloneElement(children, { id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`option-row ${centered ? "centered" : ""}`}>
|
<div className={`option-row ${centered ? "centered" : ""}`}>
|
||||||
{label && <label>{label}</label>}
|
{label && <label for={id}>{label}</label>}
|
||||||
{children}
|
{childWithId}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -49,8 +49,9 @@ function LocalizationOptions() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function LocaleSelector({ locales, currentValue, onChange }: { locales: Locale[], currentValue: string, onChange: (newLocale: string) => void }) {
|
function LocaleSelector({ id, locales, currentValue, onChange }: { id?: string; locales: Locale[], currentValue: string, onChange: (newLocale: string) => void }) {
|
||||||
return <FormSelect
|
return <FormSelect
|
||||||
|
id={id}
|
||||||
values={locales}
|
values={locales}
|
||||||
keyProperty="id" titleProperty="name"
|
keyProperty="id" titleProperty="name"
|
||||||
currentValue={currentValue} onChange={onChange}
|
currentValue={currentValue} onChange={onChange}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user