Fix UIX docs theme and popover dismiss

active-uix
dev 5 months ago
parent 3dc775f440
commit b121554918

@ -82,6 +82,7 @@ export class PopoverProvider {
readonly overlayPresence: Presence; readonly overlayPresence: Presence;
private contentRef = state<HTMLElement | null>(null); private contentRef = state<HTMLElement | null>(null);
private overlayRef = state<HTMLElement | null>(null); private overlayRef = state<HTMLElement | null>(null);
private triggerRef = state<HTMLElement | null>(null);
// tracked IDs // tracked IDs
contentId = state(''); contentId = state('');
@ -157,6 +158,10 @@ export class PopoverProvider {
this.contentRef.current = el; this.contentRef.current = el;
} }
setTriggerRef(el: HTMLElement | null) {
this.triggerRef.current = el;
}
setOverlayRef(el: HTMLElement | null) { setOverlayRef(el: HTMLElement | null) {
this.overlayRef.current = el; this.overlayRef.current = el;
} }
@ -166,7 +171,13 @@ export class PopoverProvider {
*/ */
dismissWith(action: 'save' | 'cancel' | 'dismiss' | 'dismiss-outside' | 'fail'): Promise<void> { dismissWith(action: 'save' | 'cancel' | 'dismiss' | 'dismiss-outside' | 'fail'): Promise<void> {
const eventName = action === 'fail' ? 'close-after-fail' : `close-${action}`; const eventName = action === 'fail' ? 'close-after-fail' : `close-${action}`;
return this.runtime.trigger(eventName); return this.triggerClose(eventName);
}
triggerClose(eventName: string, fallbackTarget?: HTMLElement): Promise<void> {
const target = this.contentRef.current ?? fallbackTarget ?? this.triggerRef.current;
if (!target) return this.runtime.trigger(eventName);
return this.runtime.trigger(eventName, { fallbackTarget: target });
} }
scheduleHoverOpen(delay: number, fallbackTarget: HTMLElement) { scheduleHoverOpen(delay: number, fallbackTarget: HTMLElement) {
@ -189,7 +200,7 @@ export class PopoverProvider {
delay, delay,
() => { () => {
this.hoverTimer = null; this.hoverTimer = null;
void this.runtime.trigger('close-dismiss'); void this.triggerClose('close-dismiss');
}, },
{ replace: true, meta: { component: 'popover', action: 'hover-close' } } { replace: true, meta: { component: 'popover', action: 'hover-close' } }
); );
@ -246,6 +257,7 @@ export class PopoverTriggerProvider {
id: opts.id, id: opts.id,
ref: opts.ref, ref: opts.ref,
owner: this, owner: this,
onRefChange: (el) => this.provider.setTriggerRef(el),
syncAttrs: true syncAttrs: true
}); });
@ -267,7 +279,7 @@ export class PopoverTriggerProvider {
// pass the trigger as fallbackTarget so the visual channel still has // pass the trigger as fallbackTarget so the visual channel still has
// somewhere to write `data-event*`. // somewhere to write `data-event*`.
if (this.provider.opts.open.current) { if (this.provider.opts.open.current) {
void this.provider.runtime.trigger('close-dismiss'); void this.provider.triggerClose('close-dismiss', e.currentTarget as HTMLElement);
} else { } else {
void this.provider.runtime.trigger('present', { void this.provider.runtime.trigger('present', {
fallbackTarget: e.currentTarget as HTMLElement fallbackTarget: e.currentTarget as HTMLElement
@ -279,7 +291,7 @@ export class PopoverTriggerProvider {
if (e.key === KEYS.ENTER || e.key === KEYS.SPACE) { if (e.key === KEYS.ENTER || e.key === KEYS.SPACE) {
e.preventDefault(); e.preventDefault();
if (this.provider.opts.open.current) { if (this.provider.opts.open.current) {
void this.provider.runtime.trigger('close-dismiss'); void this.provider.triggerClose('close-dismiss', e.currentTarget as HTMLElement);
} else { } else {
void this.provider.runtime.trigger('present', { void this.provider.runtime.trigger('present', {
fallbackTarget: e.currentTarget as HTMLElement fallbackTarget: e.currentTarget as HTMLElement
@ -443,12 +455,16 @@ export class PopoverContentProvider {
enabled: readableActive(() => this.provider.opts.open.current), enabled: readableActive(() => this.provider.opts.open.current),
onEscapeKeydown: readableActive(() => (e: KeyboardEvent) => { onEscapeKeydown: readableActive(() => (e: KeyboardEvent) => {
opts.onEscapeKeydown.current(e); opts.onEscapeKeydown.current(e);
if (!e.defaultPrevented) void this.provider.runtime.trigger('close-dismiss'); if (!e.defaultPrevented) {
void this.provider.triggerClose('close-dismiss', opts.ref.current ?? undefined);
}
}), }),
escapeKeydownBehavior: resolvedEscapeBehavior, escapeKeydownBehavior: resolvedEscapeBehavior,
onInteractOutside: readableActive(() => (e: PointerEvent) => { onInteractOutside: readableActive(() => (e: PointerEvent) => {
opts.onInteractOutside.current(e); opts.onInteractOutside.current(e);
if (!e.defaultPrevented) void this.provider.runtime.trigger('close-dismiss-outside'); if (!e.defaultPrevented) {
void this.provider.triggerClose('close-dismiss-outside', opts.ref.current ?? undefined);
}
}), }),
interactOutsideBehavior: resolvedInteractBehavior, interactOutsideBehavior: resolvedInteractBehavior,
onFocusOutside: opts.onFocusOutside, onFocusOutside: opts.onFocusOutside,
@ -563,8 +579,8 @@ export class PopoverCloseProvider {
}); });
} }
readonly onclick = (_e: SomaMouseEvent<HTMLButtonElement>) => { readonly onclick = (e: SomaMouseEvent<HTMLButtonElement>) => {
void this.provider.runtime.trigger('close-cancel'); void this.provider.triggerClose('close-cancel', e.currentTarget as HTMLElement);
}; };
readonly props = $derived.by(() => ({ readonly props = $derived.by(() => ({

@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onDestroy } from 'svelte';
import { createActiveUix, setActiveUix } from '$active-uix'; import { createActiveUix, setActiveUix } from '$active-uix';
import { standardPrefsDimensions } from '$prefs'; import { createActivePrefsDomProjection, standardPrefsDimensions } from '$prefs';
import { page } from '$app/state'; import { page } from '$app/state';
import { Soma } from '$soma/core/soma.svelte'; import { Soma } from '$soma/core/soma.svelte';
import { ActiveEidos } from '$uix/eidos'; import { ActiveEidos } from '$uix/eidos';
@ -44,9 +45,9 @@
} }
}); });
setActiveUix(uix); setActiveUix(uix);
Soma.create(); Soma.create();
ActiveEidos.create({ applyDom: false }); const prefsProjection = createActivePrefsDomProjection({ prefs: uix.prefs, dom: uix.dom });
type Section = type Section =
| { kind: 'top'; slug: string; label: string } | { kind: 'top'; slug: string; label: string }
@ -119,6 +120,24 @@ ActiveEidos.create({ applyDom: false });
// ── Theme ───────────────────────────────────────────────────────────── // ── Theme ─────────────────────────────────────────────────────────────
let theme = $state<'light' | 'dark'>('light'); let theme = $state<'light' | 'dark'>('light');
const eidosModeListeners = new Set<(value: 'light' | 'dark') => void>();
const activeEidos = ActiveEidos.create({
applyDom: true,
theme: 'base',
modeSource: {
get: () => theme,
onChange(handler) {
eidosModeListeners.add(handler);
return () => eidosModeListeners.delete(handler);
}
}
});
onDestroy(() => {
prefsProjection.dispose();
activeEidos.dispose();
uix.dispose();
});
$effect(() => { $effect(() => {
if (typeof document === 'undefined') return; if (typeof document === 'undefined') return;
@ -136,6 +155,11 @@ ActiveEidos.create({ applyDom: false });
localStorage.setItem('uix-docs-theme', theme); localStorage.setItem('uix-docs-theme', theme);
}); });
$effect(() => {
const currentTheme = theme;
for (const listener of eidosModeListeners) listener(currentTheme);
});
let search = $state(''); let search = $state('');
// ── Topbar prefs ────────────────────────────────────────────────────── // ── Topbar prefs ──────────────────────────────────────────────────────
@ -194,9 +218,9 @@ ActiveEidos.create({ applyDom: false });
uix.prefs.setIntent('locale', numberFormat); uix.prefs.setIntent('locale', numberFormat);
uix.prefs.setIntent('currency', currency); uix.prefs.setIntent('currency', currency);
uix.prefs.setIntent('unitSystem', units); uix.prefs.setIntent('unitSystem', units);
uix.prefs.setIntent('theme', theme);
uix.prefs.setIntent('sound', soundOn ? 'allow' : 'reduce'); uix.prefs.setIntent('sound', soundOn ? 'allow' : 'reduce');
uix.prefs.setIntent('motion', motionOn ? 'allow' : 'reduce'); uix.prefs.setIntent('motion', motionOn ? 'allow' : 'reduce');
uix.prefs.setIntent('haptic', hapticOn ? 'allow' : 'reduce');
}); });
const filteredNav = $derived.by(() => { const filteredNav = $derived.by(() => {
@ -218,7 +242,7 @@ ActiveEidos.create({ applyDom: false });
<link rel="preconnect" href="https://api.fontshare.com" /> <link rel="preconnect" href="https://api.fontshare.com" />
</svelte:head> </svelte:head>
<div data-uix-docs data-theme={theme}> <div data-uix-docs data-theme={theme} data-mode={theme}>
<div data-uix-shell> <div data-uix-shell>
<header data-uix-topbar> <header data-uix-topbar>
<a href="/uix" data-uix-brand> <a href="/uix" data-uix-brand>

Loading…
Cancel
Save

Powered by TurnKey Linux.