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