Update GeoIntel project files
This commit is contained in:
@@ -0,0 +1,811 @@
|
||||
import {
|
||||
Accessibility,
|
||||
AlignJustify,
|
||||
Check,
|
||||
CloudSun,
|
||||
Eye,
|
||||
GalleryHorizontalEnd,
|
||||
Gauge,
|
||||
Grid3X3,
|
||||
Layers3,
|
||||
Maximize2,
|
||||
MonitorCog,
|
||||
Image as ImageIcon,
|
||||
RefreshCw,
|
||||
Server,
|
||||
Sparkles,
|
||||
Trash2,
|
||||
Upload,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type {
|
||||
Accent,
|
||||
BackgroundStyle,
|
||||
ContentWidth,
|
||||
DashboardPayload,
|
||||
Density,
|
||||
Theme,
|
||||
ViewMode,
|
||||
WallpaperFit,
|
||||
} from "../../shared/contracts";
|
||||
import { api } from "../api";
|
||||
|
||||
type Performer = (
|
||||
action: () => Promise<unknown>,
|
||||
message: string,
|
||||
) => Promise<boolean>;
|
||||
|
||||
interface AppearanceSettingsProps {
|
||||
data: DashboardPayload;
|
||||
perform: Performer;
|
||||
}
|
||||
|
||||
const themes: { id: Theme; label: string; copy: string }[] = [
|
||||
{
|
||||
id: "dark",
|
||||
label: "Donker",
|
||||
copy: "Diep navy en rustig contrast.",
|
||||
},
|
||||
{ id: "light", label: "Licht", copy: "Helder, rustig en verfijnd." },
|
||||
{ id: "harbor", label: "Haven", copy: "Koel marineblauw voor late avonden." },
|
||||
];
|
||||
|
||||
const accents: { id: Accent; label: string }[] = [
|
||||
{ id: "coral", label: "Koraal" },
|
||||
{ id: "ocean", label: "Oceaan" },
|
||||
{ id: "violet", label: "Violet" },
|
||||
{ id: "lime", label: "Limoen" },
|
||||
];
|
||||
|
||||
const views: { id: ViewMode; label: string; copy: string }[] = [
|
||||
{
|
||||
id: "overview",
|
||||
label: "Volledig overzicht",
|
||||
copy: "Alle verzamelingen op één doorlopende pagina.",
|
||||
},
|
||||
{
|
||||
id: "categories",
|
||||
label: "Categorienavigatie",
|
||||
copy: "Wissel tussen gerichte verzamelingstabbladen.",
|
||||
},
|
||||
];
|
||||
|
||||
const widths: {
|
||||
id: ContentWidth;
|
||||
label: string;
|
||||
copy: string;
|
||||
icon: typeof Maximize2;
|
||||
}[] = [
|
||||
{
|
||||
id: "focused",
|
||||
label: "Gericht",
|
||||
copy: "Rustig en gecentreerd tot 1120 px.",
|
||||
icon: AlignJustify,
|
||||
},
|
||||
{
|
||||
id: "wide",
|
||||
label: "Breed",
|
||||
copy: "Evenwichtig voor alledaagse desktops.",
|
||||
icon: GalleryHorizontalEnd,
|
||||
},
|
||||
{
|
||||
id: "fluid",
|
||||
label: "Vloeiend",
|
||||
copy: "Benut ultrabrede schermen volledig.",
|
||||
icon: Maximize2,
|
||||
},
|
||||
];
|
||||
|
||||
const densities: {
|
||||
id: Density;
|
||||
label: string;
|
||||
copy: string;
|
||||
icon: typeof Gauge;
|
||||
}[] = [
|
||||
{
|
||||
id: "compact",
|
||||
label: "Compact",
|
||||
copy: "Meer services per rij.",
|
||||
icon: Grid3X3,
|
||||
},
|
||||
{
|
||||
id: "comfortable",
|
||||
label: "Comfortabel",
|
||||
copy: "Een evenwichtige standaard voor dagelijks gebruik.",
|
||||
icon: Gauge,
|
||||
},
|
||||
{
|
||||
id: "spacious",
|
||||
label: "Ruim",
|
||||
copy: "Grotere, rustigere starttegels.",
|
||||
icon: Layers3,
|
||||
},
|
||||
];
|
||||
|
||||
const backgrounds: { id: BackgroundStyle; label: string; copy: string }[] = [
|
||||
{ id: "aurora", label: "Aurora", copy: "Zachte omgevingskleur en diepte." },
|
||||
{ id: "grid", label: "Blauwdruk", copy: "Een subtiel technisch raster." },
|
||||
{ id: "minimal", label: "Minimaal", copy: "Zuivere kleur zonder textuur." },
|
||||
];
|
||||
|
||||
export function AppearanceSettings({ data, perform }: AppearanceSettingsProps) {
|
||||
const preferences = data.preferences;
|
||||
const wallpaperInput = useRef<HTMLInputElement>(null);
|
||||
const [wallpaperDraft, setWallpaperDraft] = useState<{
|
||||
fit: WallpaperFit;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
zoom: number;
|
||||
}>(() => ({
|
||||
fit: preferences.wallpaperFit,
|
||||
positionX: preferences.wallpaperPositionX,
|
||||
positionY: preferences.wallpaperPositionY,
|
||||
zoom: preferences.wallpaperZoom,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
setWallpaperDraft({
|
||||
fit: preferences.wallpaperFit,
|
||||
positionX: preferences.wallpaperPositionX,
|
||||
positionY: preferences.wallpaperPositionY,
|
||||
zoom: preferences.wallpaperZoom,
|
||||
});
|
||||
}, [
|
||||
preferences.wallpaperFit,
|
||||
preferences.wallpaperPositionX,
|
||||
preferences.wallpaperPositionY,
|
||||
preferences.wallpaperZoom,
|
||||
]);
|
||||
|
||||
const wallpaperDraftChanged =
|
||||
wallpaperDraft.fit !== preferences.wallpaperFit ||
|
||||
wallpaperDraft.positionX !== preferences.wallpaperPositionX ||
|
||||
wallpaperDraft.positionY !== preferences.wallpaperPositionY ||
|
||||
wallpaperDraft.zoom !== preferences.wallpaperZoom;
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="settings-heading">
|
||||
<p className="eyebrow">Instellingen › Weergave</p>
|
||||
<h2>Thema</h2>
|
||||
<p>Kies de visuele stijl die het beste bij je workflow past.</p>
|
||||
</header>
|
||||
<div className="theme-grid">
|
||||
{themes.map((theme) => (
|
||||
<button
|
||||
key={theme.id}
|
||||
className={`theme-card ${preferences.theme === theme.id ? "selected" : ""}`}
|
||||
aria-pressed={preferences.theme === theme.id}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() => api.updatePreferences({ theme: theme.id }),
|
||||
`Thema ${theme.label} toegepast`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className={`theme-preview ${theme.id}`}>
|
||||
<i />
|
||||
<i />
|
||||
<i />
|
||||
</span>
|
||||
<span>
|
||||
<strong>{theme.label}</strong>
|
||||
<small>{theme.copy}</small>
|
||||
</span>
|
||||
{preferences.theme === theme.id && <Check size={17} />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="canon-preference-columns">
|
||||
<section>
|
||||
<h3 className="setting-label">Dashboardmodus</h3>
|
||||
<div className="view-grid">
|
||||
{views.map((view) => (
|
||||
<button
|
||||
key={view.id}
|
||||
className={`view-card ${preferences.viewMode === view.id ? "selected" : ""}`}
|
||||
aria-pressed={preferences.viewMode === view.id}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() => api.updatePreferences({ viewMode: view.id }),
|
||||
`${view.label} geselecteerd`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<MonitorCog size={22} />
|
||||
<span>
|
||||
<strong>{view.label}</strong>
|
||||
<small>{view.copy}</small>
|
||||
</span>
|
||||
{preferences.viewMode === view.id && <Check size={17} />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h3 className="setting-label">Grid-dichtheid</h3>
|
||||
<div className="choice-grid density-grid">
|
||||
{densities.map((density) => {
|
||||
const Icon = density.icon;
|
||||
return (
|
||||
<button
|
||||
key={density.id}
|
||||
className={`option-card ${preferences.density === density.id ? "selected" : ""}`}
|
||||
aria-pressed={preferences.density === density.id}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() => api.updatePreferences({ density: density.id }),
|
||||
`Tegels ${density.label} geselecteerd`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon size={20} />
|
||||
<strong>{density.label}</strong>
|
||||
<small>{density.copy}</small>
|
||||
{preferences.density === density.id && <Check size={15} />}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<details className="appearance-advanced">
|
||||
<summary>
|
||||
<span>
|
||||
<strong>Meer personalisatie</strong>
|
||||
<small>
|
||||
Accent, canvasbreedte, achtergrond en dashboarddetails
|
||||
</small>
|
||||
</span>
|
||||
</summary>
|
||||
<div className="appearance-advanced-content">
|
||||
<h3 className="setting-label">Accentkleur</h3>
|
||||
<div className="accent-grid">
|
||||
{accents.map((accent) => (
|
||||
<button
|
||||
key={accent.id}
|
||||
className={`accent-choice ${preferences.accent === accent.id ? "selected" : ""}`}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() => api.updatePreferences({ accent: accent.id }),
|
||||
`Accent ${accent.label} toegepast`,
|
||||
)
|
||||
}
|
||||
aria-label={`Accent ${accent.label}`}
|
||||
aria-pressed={preferences.accent === accent.id}
|
||||
>
|
||||
<span className={`accent-swatch ${accent.id}`} />
|
||||
<span>{accent.label}</span>
|
||||
{preferences.accent === accent.id && <Check size={15} />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className="setting-label">Ruimtegebruik</h3>
|
||||
<div className="choice-grid three-up">
|
||||
{widths.map((width) => {
|
||||
const Icon = width.icon;
|
||||
return (
|
||||
<button
|
||||
key={width.id}
|
||||
className={`option-card ${preferences.contentWidth === width.id ? "selected" : ""}`}
|
||||
aria-pressed={preferences.contentWidth === width.id}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() => api.updatePreferences({ contentWidth: width.id }),
|
||||
`Canvas ${width.label} geselecteerd`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon size={20} />
|
||||
<strong>{width.label}</strong>
|
||||
<small>{width.copy}</small>
|
||||
{preferences.contentWidth === width.id && <Check size={15} />}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<h3 className="setting-label">Achtergrond</h3>
|
||||
<div className="background-grid">
|
||||
{backgrounds.map((background) => (
|
||||
<button
|
||||
key={background.id}
|
||||
className={`background-card ${background.id} ${preferences.backgroundStyle === background.id ? "selected" : ""}`}
|
||||
aria-pressed={preferences.backgroundStyle === background.id}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() =>
|
||||
api.updatePreferences({ backgroundStyle: background.id }),
|
||||
`Achtergrond ${background.label} geselecteerd`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<span />
|
||||
<strong>{background.label}</strong>
|
||||
<small>{background.copy}</small>
|
||||
{preferences.backgroundStyle === background.id && (
|
||||
<Check size={15} />
|
||||
)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<section className="wallpaper-settings">
|
||||
<header className="wallpaper-editor-heading">
|
||||
<span className="wallpaper-editor-icon">
|
||||
<ImageIcon size={20} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Wallpaperstudio</strong>
|
||||
<small>
|
||||
Stem de uitsnede af op het zichtbare centrale vlak. De preview
|
||||
reageert direct; pas toe wanneer het beeld goed staat.
|
||||
</small>
|
||||
</span>
|
||||
<div className="wallpaper-actions">
|
||||
<input
|
||||
ref={wallpaperInput}
|
||||
className="sr-only"
|
||||
type="file"
|
||||
accept="image/png,image/jpeg,image/webp"
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (!file) return;
|
||||
void perform(
|
||||
() => api.uploadWallpaper(file),
|
||||
"Wallpaper bijgewerkt",
|
||||
);
|
||||
event.target.value = "";
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary-button"
|
||||
onClick={() => wallpaperInput.current?.click()}
|
||||
>
|
||||
<Upload size={16} />{" "}
|
||||
{data.wallpaper ? "Vervangen" : "Uploaden"}
|
||||
</button>
|
||||
{data.wallpaper && (
|
||||
<button
|
||||
type="button"
|
||||
className="ghost-danger-button"
|
||||
onClick={() =>
|
||||
void perform(
|
||||
() => api.removeWallpaper(),
|
||||
"Wallpaper verwijderd",
|
||||
)
|
||||
}
|
||||
>
|
||||
<Trash2 size={16} /> Verwijderen
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div
|
||||
className={`wallpaper-editor-preview ${preferences.backgroundStyle} ${data.wallpaper ? "has-wallpaper" : ""}`}
|
||||
aria-label="Voorbeeld van de dashboardwallpaper"
|
||||
>
|
||||
{data.wallpaper ? (
|
||||
<>
|
||||
<img
|
||||
className="wallpaper-preview-image"
|
||||
src={data.wallpaper.url}
|
||||
alt="Huidige dashboardwallpaper"
|
||||
style={{
|
||||
objectFit: wallpaperDraft.fit,
|
||||
objectPosition: `${wallpaperDraft.positionX}% ${wallpaperDraft.positionY}%`,
|
||||
transform: `scale(${wallpaperDraft.zoom / 100})`,
|
||||
transformOrigin: `${wallpaperDraft.positionX}% ${wallpaperDraft.positionY}%`,
|
||||
}}
|
||||
/>
|
||||
<img
|
||||
className="wallpaper-preview-ambient"
|
||||
src={data.wallpaper.url}
|
||||
alt=""
|
||||
aria-hidden="true"
|
||||
style={{
|
||||
objectPosition: `${wallpaperDraft.positionX}% ${wallpaperDraft.positionY}%`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<span className="wallpaper-preview-empty">
|
||||
<ImageIcon size={30} />
|
||||
<small>Upload eerst een PNG, JPEG of WebP tot 8 MB.</small>
|
||||
</span>
|
||||
)}
|
||||
<span className="wallpaper-preview-overlay" />
|
||||
<span className="wallpaper-preview-sidebar" />
|
||||
<span className="wallpaper-preview-topbar" />
|
||||
<span className="wallpaper-preview-card one" />
|
||||
<span className="wallpaper-preview-card two" />
|
||||
<span className="wallpaper-preview-card three" />
|
||||
</div>
|
||||
|
||||
<div className="wallpaper-editor-controls">
|
||||
<div className="wallpaper-fit-control">
|
||||
<span>
|
||||
<strong>Schaalmethode</strong>
|
||||
<small>
|
||||
Passend toont de volledige afbeelding; Vullend benut het
|
||||
volledige vlak.
|
||||
</small>
|
||||
</span>
|
||||
<div className="segmented-control" aria-label="Schaalmethode">
|
||||
{(
|
||||
[
|
||||
["contain", "Passend"],
|
||||
["cover", "Vullend"],
|
||||
] as const
|
||||
).map(([fit, label]) => (
|
||||
<button
|
||||
key={fit}
|
||||
type="button"
|
||||
className={wallpaperDraft.fit === fit ? "active" : ""}
|
||||
aria-pressed={wallpaperDraft.fit === fit}
|
||||
onClick={() =>
|
||||
setWallpaperDraft((current) => ({ ...current, fit }))
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="wallpaper-slider-grid">
|
||||
<label>
|
||||
<span>
|
||||
Horizontale focus{" "}
|
||||
<strong>{wallpaperDraft.positionX}%</strong>
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={wallpaperDraft.positionX}
|
||||
aria-label="Horizontale wallpaperfocus"
|
||||
onChange={(event) =>
|
||||
setWallpaperDraft((current) => ({
|
||||
...current,
|
||||
positionX: Number(event.target.value),
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
Verticale focus <strong>{wallpaperDraft.positionY}%</strong>
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="100"
|
||||
value={wallpaperDraft.positionY}
|
||||
aria-label="Verticale wallpaperfocus"
|
||||
onChange={(event) =>
|
||||
setWallpaperDraft((current) => ({
|
||||
...current,
|
||||
positionY: Number(event.target.value),
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span>
|
||||
Zoom <strong>{wallpaperDraft.zoom}%</strong>
|
||||
</span>
|
||||
<input
|
||||
type="range"
|
||||
min="80"
|
||||
max="160"
|
||||
value={wallpaperDraft.zoom}
|
||||
aria-label="Wallpaperzoom"
|
||||
onChange={(event) =>
|
||||
setWallpaperDraft((current) => ({
|
||||
...current,
|
||||
zoom: Number(event.target.value),
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="wallpaper-editor-footer">
|
||||
<small>
|
||||
De dashboardwallpaper blijft vast in het zichtbare centrale
|
||||
vlak terwijl de inhoud scrolt.
|
||||
</small>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary-button"
|
||||
onClick={() =>
|
||||
setWallpaperDraft({
|
||||
fit: "contain",
|
||||
positionX: 50,
|
||||
positionY: 50,
|
||||
zoom: 100,
|
||||
})
|
||||
}
|
||||
>
|
||||
Herstellen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button"
|
||||
disabled={!data.wallpaper || !wallpaperDraftChanged}
|
||||
onClick={() =>
|
||||
void perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
wallpaperFit: wallpaperDraft.fit,
|
||||
wallpaperPositionX: wallpaperDraft.positionX,
|
||||
wallpaperPositionY: wallpaperDraft.positionY,
|
||||
wallpaperZoom: wallpaperDraft.zoom,
|
||||
}),
|
||||
"Wallpaperweergave toegepast",
|
||||
)
|
||||
}
|
||||
>
|
||||
<Check size={16} /> Toepassen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h3 className="setting-label">Dashboarddetails</h3>
|
||||
<div className="toggle-list">
|
||||
<article>
|
||||
<span className="toggle-icon">
|
||||
<CloudSun size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Weer in de topbalk</strong>
|
||||
<small>
|
||||
Toon actuele temperatuur en weersituatie voor Mol.
|
||||
</small>
|
||||
</span>
|
||||
<button
|
||||
className={`switch ${preferences.weatherEnabled ? "on" : ""}`}
|
||||
role="switch"
|
||||
aria-label="Weer in de topbalk tonen"
|
||||
aria-checked={preferences.weatherEnabled}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
weatherEnabled: !preferences.weatherEnabled,
|
||||
}),
|
||||
preferences.weatherEnabled
|
||||
? "Weer verborgen"
|
||||
: "Weer getoond",
|
||||
)
|
||||
}
|
||||
>
|
||||
<span />
|
||||
</button>
|
||||
</article>
|
||||
<article className="weather-location-row">
|
||||
<span className="toggle-icon">
|
||||
<CloudSun size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Weerlocatie</strong>
|
||||
<small>Naam en coördinaten voor de weerkaart.</small>
|
||||
</span>
|
||||
<div className="weather-location-fields">
|
||||
<input
|
||||
aria-label="Naam weerlocatie"
|
||||
defaultValue={preferences.weatherLocation}
|
||||
onBlur={(event) => {
|
||||
const value = event.target.value.trim();
|
||||
if (value && value !== preferences.weatherLocation)
|
||||
void perform(
|
||||
() => api.updatePreferences({ weatherLocation: value }),
|
||||
"Weerlocatie bijgewerkt",
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
aria-label="Breedtegraad"
|
||||
type="number"
|
||||
step="0.001"
|
||||
min="-90"
|
||||
max="90"
|
||||
defaultValue={preferences.weatherLatitude}
|
||||
onBlur={(event) => {
|
||||
const value = Number(event.target.value);
|
||||
if (Number.isFinite(value))
|
||||
void perform(
|
||||
() => api.updatePreferences({ weatherLatitude: value }),
|
||||
"Breedtegraad bijgewerkt",
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
aria-label="Lengtegraad"
|
||||
type="number"
|
||||
step="0.001"
|
||||
min="-180"
|
||||
max="180"
|
||||
defaultValue={preferences.weatherLongitude}
|
||||
onBlur={(event) => {
|
||||
const value = Number(event.target.value);
|
||||
if (Number.isFinite(value))
|
||||
void perform(
|
||||
() =>
|
||||
api.updatePreferences({ weatherLongitude: value }),
|
||||
"Lengtegraad bijgewerkt",
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</article>
|
||||
<article>
|
||||
<span className="toggle-icon">
|
||||
<Sparkles size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Welkomstblok</strong>
|
||||
<small>
|
||||
Toon de begroeting, het overzicht en snelle statistieken.
|
||||
</small>
|
||||
</span>
|
||||
<button
|
||||
className={`switch ${preferences.showHero ? "on" : ""}`}
|
||||
role="switch"
|
||||
aria-label="Welkomstblok tonen"
|
||||
aria-checked={preferences.showHero}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
showHero: !preferences.showHero,
|
||||
}),
|
||||
preferences.showHero
|
||||
? "Welkomstblok verborgen"
|
||||
: "Welkomstblok getoond",
|
||||
)
|
||||
}
|
||||
>
|
||||
<span />
|
||||
</button>
|
||||
</article>
|
||||
<article>
|
||||
<span className="toggle-icon">
|
||||
<Eye size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Servicestatus</strong>
|
||||
<small>
|
||||
Toon beschikbaarheidstekst en statusindicatoren op tegels.
|
||||
</small>
|
||||
</span>
|
||||
<button
|
||||
className={`switch ${preferences.showStatus ? "on" : ""}`}
|
||||
role="switch"
|
||||
aria-label="Servicestatus tonen"
|
||||
aria-checked={preferences.showStatus}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
showStatus: !preferences.showStatus,
|
||||
}),
|
||||
preferences.showStatus
|
||||
? "Servicestatus verborgen"
|
||||
: "Servicestatus getoond",
|
||||
)
|
||||
}
|
||||
>
|
||||
<span />
|
||||
</button>
|
||||
</article>
|
||||
<article>
|
||||
<span className="toggle-icon">
|
||||
<Server size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Unraid-serverpaneel</strong>
|
||||
<small>
|
||||
Toon hostcapaciteit, containerstatus en Docker-details.
|
||||
</small>
|
||||
</span>
|
||||
<button
|
||||
className={`switch ${preferences.showServerWidget ? "on" : ""}`}
|
||||
role="switch"
|
||||
aria-label="Unraid-serverpaneel tonen"
|
||||
aria-checked={preferences.showServerWidget}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
showServerWidget: !preferences.showServerWidget,
|
||||
}),
|
||||
preferences.showServerWidget
|
||||
? "Unraid-paneel verborgen"
|
||||
: "Unraid-paneel getoond",
|
||||
)
|
||||
}
|
||||
>
|
||||
<span />
|
||||
</button>
|
||||
</article>
|
||||
<article>
|
||||
<span className="toggle-icon">
|
||||
<Accessibility size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Verminderde beweging</strong>
|
||||
<small>
|
||||
Schakel decoratieve animaties en vloeiende overgangen uit.
|
||||
</small>
|
||||
</span>
|
||||
<button
|
||||
className={`switch ${preferences.reducedMotion ? "on" : ""}`}
|
||||
role="switch"
|
||||
aria-label="Verminderde beweging"
|
||||
aria-checked={preferences.reducedMotion}
|
||||
onClick={() =>
|
||||
perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
reducedMotion: !preferences.reducedMotion,
|
||||
}),
|
||||
preferences.reducedMotion
|
||||
? "Animaties ingeschakeld"
|
||||
: "Beweging verminderd",
|
||||
)
|
||||
}
|
||||
>
|
||||
<span />
|
||||
</button>
|
||||
</article>
|
||||
<article>
|
||||
<span className="toggle-icon">
|
||||
<RefreshCw size={18} />
|
||||
</span>
|
||||
<span>
|
||||
<strong>Verversingsinterval</strong>
|
||||
<small>
|
||||
Hoe vaak DockDeck statussen en actieve panelen opnieuw
|
||||
ophaalt.
|
||||
</small>
|
||||
</span>
|
||||
<label className="inline-select-control">
|
||||
<span className="sr-only">Verversingsinterval</span>
|
||||
<select
|
||||
name="pollingIntervalMs"
|
||||
value={preferences.pollingIntervalMs}
|
||||
aria-label="Verversingsinterval"
|
||||
onChange={(event) =>
|
||||
void perform(
|
||||
() =>
|
||||
api.updatePreferences({
|
||||
pollingIntervalMs: Number(event.target.value),
|
||||
}),
|
||||
"Verversingsinterval bijgewerkt",
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value={10000}>10 seconden</option>
|
||||
<option value={30000}>30 seconden</option>
|
||||
<option value={60000}>1 minuut</option>
|
||||
<option value={120000}>2 minuten</option>
|
||||
<option value={300000}>5 minuten</option>
|
||||
</select>
|
||||
</label>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user