Files
WPS3Media/ui/components/Panel.svelte
Malin 3248cbb029 feat: add S3-compatible storage provider (MinIO, Ceph, R2, etc.)
Adds a new 'S3-Compatible Storage' provider that works with any
S3-API-compatible object storage service, including MinIO, Ceph,
Cloudflare R2, Backblaze B2, and others.

Changes:
- New provider class: classes/providers/storage/s3-compatible-provider.php
  - Provider key: s3compatible
  - Reads user-configured endpoint URL from settings
  - Uses path-style URL access (required by most S3-compatible services)
  - Supports credentials via AS3CF_S3COMPAT_ACCESS_KEY_ID /
    AS3CF_S3COMPAT_SECRET_ACCESS_KEY wp-config.php constants
  - Disables AWS-specific features (Block Public Access, Object Ownership)
- New provider SVG icons (s3compatible.svg, -link.svg, -round.svg)
- Registered provider in main plugin class with endpoint setting support
- Updated StorageProviderSubPage to show endpoint URL input for S3-compatible
- Built pro settings bundle with rollup (Svelte 4.2.19)
- Added package.json and updated rollup.config.mjs for pro-only builds
2026-03-03 12:30:18 +01:00

140 lines
3.8 KiB
Svelte

<script>
import {createEventDispatcher, getContext, hasContext} from "svelte";
import {writable} from "svelte/store";
import {fade} from "svelte/transition";
import {link} from "svelte-spa-router";
import {defined_settings, strings} from "../js/stores";
import PanelContainer from "./PanelContainer.svelte";
import PanelRow from "./PanelRow.svelte";
import DefinedInWPConfig from "./DefinedInWPConfig.svelte";
import ToggleSwitch from "./ToggleSwitch.svelte";
import HelpButton from "./HelpButton.svelte";
import Button from "./Button.svelte";
const classes = $$props.class ? $$props.class : "";
const dispatch = createEventDispatcher();
export let ref = {};
export let name = "";
export let heading = "";
export let defined = false;
export let multi = false;
export let flyout = false;
export let toggleName = "";
export let toggle = false;
export let refresh = false;
export let refreshText = $strings.refresh_title;
export let refreshDesc = refreshText;
export let refreshing = false;
export let helpKey = "";
export let helpURL = "";
export let helpDesc = $strings.help_desc;
// We can display storage provider info on the right-hand side of the panel's header.
// In the future, if anything else needs to be displayed in the same position we
// should create a named slot or assignable component. CSS changes would be required.
export let storageProvider = null;
// Parent page may want to be locked.
let settingsLocked = writable( false );
if ( hasContext( "settingsLocked" ) ) {
settingsLocked = getContext( "settingsLocked" );
}
$: locked = $settingsLocked;
$: toggleDisabled = $defined_settings.includes( toggleName ) || locked;
/**
* If appropriate, clicking the header toggles to toggle switch.
*/
function headingClickHandler() {
if ( toggleName && !toggleDisabled ) {
toggle = !toggle;
}
}
/**
* Catch escape key and emit a custom cancel event.
*
* @param {KeyboardEvent} event
*/
function handleKeyup( event ) {
if ( event.key === "Escape" ) {
event.preventDefault();
dispatch( "cancel" );
}
}
</script>
<!-- TODO: Fix a11y. -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="panel {name}"
class:multi
class:flyout
class:locked
transition:fade={{duration: flyout ? 200 : 0}}
bind:this={ref}
on:focusout
on:mouseenter
on:mouseleave
on:mousedown
on:click
on:keyup={handleKeyup}
>
{#if !multi && heading}
<div class="heading">
<h2>{heading}</h2>
{#if helpURL}
<HelpButton url={helpURL} desc={helpDesc}/>
{:else if helpKey}
<HelpButton key={helpKey} desc={helpDesc}/>
{/if}
<DefinedInWPConfig {defined}/>
</div>
{/if}
<PanelContainer class={classes}>
{#if multi && heading}
<PanelRow header>
{#if toggleName}
<ToggleSwitch name={toggleName} bind:checked={toggle} disabled={toggleDisabled}>
{heading}
</ToggleSwitch>
<!-- TODO: Fix a11y. -->
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<h3 on:click={headingClickHandler} class="toggler" class:toggleDisabled>{heading}</h3>
{:else}
<h3>{heading}</h3>
{/if}
<DefinedInWPConfig {defined}/>
{#if refresh}
<Button refresh {refreshing} title={refreshDesc} on:click={() => dispatch("refresh")}>{@html refreshText}</Button>
{/if}
{#if storageProvider}
<div class="provider">
<a href="/storage/provider" use:link class="link">
<img src={storageProvider.link_icon} alt={storageProvider.icon_desc}>
{storageProvider.provider_service_name}
</a>
</div>
{/if}
{#if helpURL}
<HelpButton url={helpURL} desc={helpDesc}/>
{:else if helpKey}
<HelpButton key={helpKey} desc={helpDesc}/>
{/if}
</PanelRow>
{/if}
<slot/>
</PanelContainer>
</div>
<style>
.toggler:not(.toggleDisabled) {
cursor: pointer;
}
</style>