svelte-headless-components
Advanced tools
Comparing version 0.0.8 to 0.0.9
@@ -6,5 +6,21 @@ import { type ComputeConfig, type ContentAction, type ReferenceAction, type UpdatePosition } from 'svelte-floating-ui'; | ||
export declare function isElementInPopover(srcElement: EventTarget | null): boolean; | ||
export type Options = { | ||
export declare function defaultVirtualPosition(): { | ||
getBoundingClientRect: () => { | ||
x: number; | ||
y: number; | ||
top: number; | ||
left: number; | ||
bottom: number; | ||
right: number; | ||
width: number; | ||
height: number; | ||
}; | ||
}; | ||
export declare function setVirtualPosition(element: Writable<VirtualElement>, x: number, y: number): void; | ||
export type TriggerEvent = 'mouseup' | 'contextmenu'; | ||
export type Config = { | ||
triggerEvent?: TriggerEvent; | ||
virtualElement?: Writable<VirtualElement>; | ||
floatingUi?: Partial<ComputeConfig>; | ||
}; | ||
export declare function createPopover(isOpen: Writable<boolean>, options?: Options): [ReferenceAction, ContentAction, UpdatePosition]; | ||
export declare function createPopover(isOpen: Writable<boolean>, config?: Config): [ReferenceAction, ContentAction, UpdatePosition]; |
@@ -1,5 +0,6 @@ | ||
import { createId } from '@paralleldrive/cuid2'; | ||
import { BROWSER } from 'esm-env'; | ||
import { createFloatingActions, } from 'svelte-floating-ui'; | ||
import { flip } from 'svelte-floating-ui/dom'; | ||
import { flip, offset, shift } from 'svelte-floating-ui/dom'; | ||
import { get } from 'svelte/store'; | ||
import { getBackdropNode, hideBackdrop, showBackdrop } from './backdrop.js'; | ||
export function isElementInPopover(srcElement) { | ||
@@ -10,34 +11,90 @@ if (!srcElement) | ||
} | ||
const defaultFloatingUiOptions = { | ||
strategy: 'absolute', | ||
placement: 'bottom-start', | ||
middleware: [flip()], | ||
}; | ||
export function createPopover(isOpen, options) { | ||
const id = createId(); | ||
export function defaultVirtualPosition() { | ||
return { | ||
getBoundingClientRect: () => { | ||
return { | ||
x: 0, | ||
y: 0, | ||
top: 0, | ||
left: 0, | ||
bottom: 0, | ||
right: 0, | ||
width: 0, | ||
height: 0, | ||
}; | ||
}, | ||
}; | ||
} | ||
export function setVirtualPosition(element, x, y) { | ||
element.set({ | ||
getBoundingClientRect: () => { | ||
return { | ||
x, | ||
y, | ||
top: y, | ||
left: x, | ||
bottom: y, | ||
right: x, | ||
width: 0, | ||
height: 0, | ||
}; | ||
}, | ||
}); | ||
} | ||
function defaultFloatingUiConfig(triggerEvent) { | ||
return triggerEvent === 'mouseup' | ||
? { | ||
strategy: 'absolute', | ||
placement: 'bottom-start', | ||
middleware: [flip()], | ||
} | ||
: { | ||
strategy: 'absolute', | ||
placement: 'right-start', | ||
middleware: [offset(8), shift()], | ||
}; | ||
} | ||
export function createPopover(isOpen, config) { | ||
function customTrigger(node) { | ||
function triggerClick(e) { | ||
isOpen.update((value) => !value); | ||
if (isBackdropClicking && triggerEvent !== 'contextmenu') { | ||
return; | ||
} | ||
if (triggerEvent === 'contextmenu') { | ||
e.preventDefault(); | ||
} | ||
isOpen.set(true); | ||
if (config?.virtualElement) { | ||
setVirtualPosition(config.virtualElement, e.clientX, e.clientY); | ||
} | ||
} | ||
function clickOutside(e) { | ||
const clickedNode = e.target.closest(`[data-popover-id="${id}"]`); | ||
let clickedNodeId; | ||
if (clickedNode) { | ||
clickedNodeId = clickedNode.dataset.popoverId; | ||
function backdropMouseDown(e) { | ||
if (e.buttons === 1) { | ||
isBackdropClicking = true; | ||
} | ||
if (node instanceof HTMLElement && clickedNodeId !== id && !e.defaultPrevented && get(isOpen)) { | ||
else if (get(isOpen)) { | ||
isOpen.set(false); | ||
isBackdropClicking = false; | ||
} | ||
} | ||
if (node instanceof HTMLElement) { | ||
node.dataset.popoverId = id; | ||
node.addEventListener('mouseup', triggerClick); | ||
function backdropMouseUp() { | ||
if (isBackdropClicking) { | ||
isOpen.set(false); | ||
} | ||
isBackdropClicking = false; | ||
} | ||
document.addEventListener('mouseup', clickOutside); | ||
let isBackdropClicking = false; | ||
const backdropNode = getBackdropNode(); | ||
if (BROWSER) { | ||
node.addEventListener(triggerEvent, triggerClick); | ||
backdropNode?.addEventListener('mousedown', backdropMouseDown); | ||
backdropNode?.addEventListener('click', backdropMouseUp); | ||
} | ||
return { | ||
destroy: () => { | ||
if (node instanceof HTMLElement) { | ||
node.removeEventListener('mouseup', triggerClick); | ||
if (BROWSER) { | ||
node.removeEventListener(triggerEvent, triggerClick); | ||
backdropNode?.removeEventListener('mousedown', backdropMouseDown); | ||
backdropNode?.removeEventListener('click', backdropMouseUp); | ||
} | ||
document.removeEventListener('mouseup', clickOutside); | ||
}, | ||
@@ -47,14 +104,24 @@ }; | ||
function customContent(node) { | ||
node.dataset.popoverId = id; | ||
showBackdrop(node); | ||
return { | ||
destroy: () => { }, | ||
destroy: () => { | ||
console.log('destroy:customContent'); | ||
}, | ||
}; | ||
} | ||
const triggerEvent = config?.triggerEvent ?? 'mouseup'; | ||
isOpen.subscribe((value) => { | ||
if (!value) { | ||
hideBackdrop(); | ||
} | ||
}); | ||
const [floatingTrigger, floatingContent, update] = createFloatingActions({ | ||
...defaultFloatingUiOptions, | ||
...options?.floatingUi, | ||
...defaultFloatingUiConfig(triggerEvent), | ||
...config?.floatingUi, | ||
}); | ||
const triggerActions = [customTrigger, floatingTrigger]; | ||
function triggerAction(node) { | ||
const destructors = triggerActions.map((action) => action(node)); | ||
const destructors = [ | ||
customTrigger(node), | ||
config?.virtualElement ? floatingTrigger(config.virtualElement) : floatingTrigger(node), | ||
]; | ||
return { | ||
@@ -61,0 +128,0 @@ destroy() { |
@@ -33,4 +33,7 @@ import type { ComputeConfig } from 'svelte-floating-ui'; | ||
minSearchLength: number; | ||
triggerEvent?: 'contextmenu' | 'mouseup'; | ||
activeOnOpen?: boolean; | ||
additions: Addition[]; | ||
floatingUiOptions?: Partial<ComputeConfig>; | ||
useVirtualElement?: boolean; | ||
}; | ||
@@ -37,0 +40,0 @@ export type AddOption = { |
import { BROWSER } from 'esm-env'; | ||
import { get, writable } from 'svelte/store'; | ||
import { createPopover } from '../popover/popover.js'; | ||
import { createPopover, defaultVirtualPosition } from '../popover/popover.js'; | ||
const searchOptionPrefix = 'search'; | ||
@@ -49,3 +49,13 @@ export const element = (node, input) => { | ||
constructor(inputOptions, inputConfig = {}) { | ||
const config = { ...{ additions: [], closeOnSelect: 'not_multi', minSearchLength: 1 }, ...inputConfig }; | ||
const config = { | ||
...{ | ||
additions: [], | ||
closeOnSelect: 'not_multi', | ||
minSearchLength: 1, | ||
activeOnOpen: true, | ||
useVirtualElement: false, | ||
triggerEvent: 'mouseup', | ||
}, | ||
...inputConfig, | ||
}; | ||
const options = inputOptions.map((option) => Select.inputToOptionItem(option)); | ||
@@ -105,3 +115,3 @@ this.inputOptions = writable(inputOptions); | ||
}); | ||
this.setUpFloatingUi(config.floatingUiOptions); | ||
this.setUpFloatingUi(config); | ||
if (BROWSER) { | ||
@@ -113,8 +123,13 @@ document.addEventListener('keydown', (e) => this.onKeyDown(e)); | ||
open() { | ||
const selected = get(this.state.selected); | ||
if (selected.length > 0) { | ||
this.setActive(selected[0]); | ||
if (this.config.activeOnOpen) { | ||
const selected = get(this.state.selected); | ||
if (selected.length > 0) { | ||
this.setActive(selected[0]); | ||
} | ||
else { | ||
this.setFirstActive(); | ||
} | ||
} | ||
else { | ||
this.setFirstActive(); | ||
this.setActive(null); | ||
} | ||
@@ -126,3 +141,2 @@ get(this.elements.trigger)?.blur(); | ||
this.setActive(null); | ||
get(this.elements.trigger)?.focus(); | ||
} | ||
@@ -473,6 +487,2 @@ selectOption(id) { | ||
if (isOpen) { | ||
const activeOption = this.getActiveLeaf(); | ||
if (!activeOption) { | ||
return; | ||
} | ||
switch (event.code) { | ||
@@ -482,3 +492,7 @@ case 'Escape': | ||
break; | ||
case 'Enter': | ||
case 'Enter': { | ||
const activeOption = this.getActiveLeaf(); | ||
if (!activeOption) { | ||
return; | ||
} | ||
if (activeOption.type === 'select') { | ||
@@ -498,2 +512,3 @@ const addOptions = get(this.state.additionOptions); | ||
break; | ||
} | ||
case 'ArrowDown': | ||
@@ -523,6 +538,8 @@ this.setNextActive(); | ||
} | ||
setUpFloatingUi(config = {}) { | ||
const isOpen = this.state.isOpen; | ||
const [floatingTrigger, floatingContent] = createPopover(isOpen, { | ||
floatingUi: config, | ||
setUpFloatingUi(config) { | ||
let virtualElement = config.useVirtualElement ? writable(defaultVirtualPosition()) : undefined; | ||
const [floatingTrigger, floatingContent] = createPopover(this.state.isOpen, { | ||
floatingUi: config.floatingUiOptions, | ||
virtualElement, | ||
triggerEvent: config.triggerEvent, | ||
}); | ||
@@ -529,0 +546,0 @@ this.elements.trigger.subscribe((node) => { |
{ | ||
"name": "svelte-headless-components", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
48183
25
1032