feat: share post to other apps

This commit is contained in:
gosangam
2026-05-31 08:04:46 +05:30
parent 03b8f85e06
commit 336f478f8b
13 changed files with 1290 additions and 82 deletions
+82 -48
View File
@@ -235,11 +235,23 @@ type TriggerRenderer = (state: {
* the popover off the bottom of the viewport. */
type Placement = "below-right" | "above-left";
/** How the controls are presented.
*
* - "popover" (default) — the icon trigger + floating panel pattern
* used in SiteHeader/sidebar. Centred triggers on narrow viewports
* can push the panel off-screen, which is why auth users get the
* dedicated settings page instead.
* - "inline" — render the controls flat as part of normal page flow,
* full-width up to a sensible cap. Used by /u/me/settings. */
type Variant = "popover" | "inline";
export function ReadingSettings({
variant = "popover",
renderTrigger,
placement = "below-right",
onOpenChange,
}: {
variant?: Variant;
renderTrigger?: TriggerRenderer;
placement?: Placement;
/** Fires every time the popover opens or closes. Used by the sidebar
@@ -456,6 +468,9 @@ export function ReadingSettings({
visiblePalettes.find((p) => p.id === activePaletteId)?.name ?? "";
if (!mounted) {
// Inline variant: render nothing until mounted so we don't flash a
// misaligned skeleton on the dedicated settings page.
if (variant === "inline") return null;
return (
<Button variant="ghost" size="icon-sm" aria-label="Reading settings">
<Settings className="size-4" />
@@ -463,53 +478,9 @@ export function ReadingSettings({
);
}
const toggle = () => setOpen((v) => !v);
return (
<div className="relative">
{renderTrigger ? (
renderTrigger({ open, toggle, buttonRef })
) : (
<button
ref={buttonRef as React.Ref<HTMLButtonElement>}
aria-label="Reading settings"
aria-expanded={open}
onClick={toggle}
className={cn(
"size-9 inline-flex items-center justify-center rounded-lg",
"text-muted-foreground hover:text-foreground hover:bg-muted",
"transition-colors outline-none",
"focus-visible:ring-2 focus-visible:ring-ring",
)}
>
<Settings className="size-5 md:size-4" />
</button>
)}
{open && (
<MaybePortal portal={placement === "above-left" && portalPos !== null}>
<div
ref={panelRef}
style={
placement === "above-left" && portalPos
? {
position: "fixed",
left: portalPos.left,
bottom: portalPos.bottom,
}
: undefined
}
className={cn(
"w-72 rounded-lg border border-border bg-card shadow-lg p-4",
"max-h-[80vh] overflow-y-auto",
placement === "above-left" && portalPos
? "z-[100]"
: placement === "above-left"
? "absolute bottom-full left-0 mb-2 z-50"
: "absolute top-full right-0 mt-2 z-50",
)}
>
{/* Theme selector */}
const panelInner = (
<>
{/* Theme selector */}
<div className="mb-4">
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
Theme
@@ -635,7 +606,70 @@ export function ReadingSettings({
</div>
</div>
</div>
</div>
</>
);
// ── Inline variant ──────────────────────────────────────────────────
// Renders the same controls as a flush, full-width card. No popover
// positioning, so it can't drift off-viewport on a narrow screen. Used
// by /u/me/settings.
if (variant === "inline") {
return (
<div className="w-full max-w-xl rounded-lg border border-border bg-card p-4 sm:p-5">
{panelInner}
</div>
);
}
// ── Popover variant (default) ───────────────────────────────────────
const toggle = () => setOpen((v) => !v);
return (
<div className="relative">
{renderTrigger ? (
renderTrigger({ open, toggle, buttonRef })
) : (
<button
ref={buttonRef as React.Ref<HTMLButtonElement>}
aria-label="Reading settings"
aria-expanded={open}
onClick={toggle}
className={cn(
"size-9 inline-flex items-center justify-center rounded-lg",
"text-muted-foreground hover:text-foreground hover:bg-muted",
"transition-colors outline-none",
"focus-visible:ring-2 focus-visible:ring-ring",
)}
>
<Settings className="size-5 md:size-4" />
</button>
)}
{open && (
<MaybePortal portal={placement === "above-left" && portalPos !== null}>
<div
ref={panelRef}
style={
placement === "above-left" && portalPos
? {
position: "fixed",
left: portalPos.left,
bottom: portalPos.bottom,
}
: undefined
}
className={cn(
"w-72 rounded-lg border border-border bg-card shadow-lg p-4",
"max-h-[80vh] overflow-y-auto",
placement === "above-left" && portalPos
? "z-[100]"
: placement === "above-left"
? "absolute bottom-full left-0 mb-2 z-50"
: "absolute top-full right-0 mt-2 z-50",
)}
>
{panelInner}
</div>
</MaybePortal>
)}
</div>