UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

96 lines 4.85 kB
import { Shade, createComponent } from '@furystack/shades'; import { ClickAwayService } from '../../services/click-away-service.js'; import { cssVariableTheme } from '../../services/css-variable-theme.js'; import { Icon } from '../icons/icon.js'; import { close } from '../icons/icon-definitions.js'; import { Loader } from '../loader.js'; import { searchableInputStyles } from '../searchable-input-styles.js'; import { CommandPaletteInput } from './command-palette-input.js'; import { CommandPaletteManager } from './command-palette-manager.js'; import { CommandPaletteSuggestionList } from './command-palette-suggestion-list.js'; export * from './command-palette-input.js'; export * from './command-palette-manager.js'; export * from './command-palette-suggestion-list.js'; export * from './command-provider.js'; export const CommandPalette = Shade({ customElementName: 'shade-command-palette', css: { ...searchableInputStyles, fontFamily: cssVariableTheme.typography.fontFamily, '& .command-palette-wrapper': { display: 'flex', flexDirection: 'column', }, '& .loader-container': { width: '20px', height: '20px', opacity: '0', }, '&[data-loading] .loader-container': { opacity: '1', }, }, render: ({ props, injector, useState, useDisposable, useObservable, useHostProps, useRef }) => { const [manager] = useState('manager', new CommandPaletteManager(props.commandProviders)); const wrapperRef = useRef('wrapper'); useDisposable('clickAwayService', () => { // Defer to next microtask so the ref is populated after mount let clickAway = null; queueMicrotask(() => { const hostEl = wrapperRef.current?.closest('shade-command-palette'); if (hostEl) { clickAway = new ClickAwayService(hostEl, () => manager.isOpened.setValue(false)); } }); return { [Symbol.dispose]: () => clickAway?.[Symbol.dispose]() }; }); const [isLoading] = useObservable('isLoading', manager.isLoading); const [isOpenedAtRender, setIsOpened] = useObservable('isOpened', manager.isOpened); useHostProps({ ...(isLoading ? { 'data-loading': '' } : {}), ...(isOpenedAtRender ? { 'data-opened': '' } : {}), tabIndex: -1, 'data-spatial-nav-target': '', onfocus: (ev) => { const host = ev.currentTarget; const input = host.querySelector('input'); if (input) { input.focus(); } }, }); return (createComponent("div", { ref: wrapperRef, className: "command-palette-wrapper", onkeydown: (ev) => { const hasSuggestions = manager.isOpened.getValue() && manager.currentSuggestions.getValue().length > 0; if (!hasSuggestions) return; if (ev.key === 'Enter') { ev.preventDefault(); manager.selectSuggestion(injector); return; } if (ev.key === 'ArrowUp') { ev.preventDefault(); manager.selectedIndex.setValue(Math.max(0, manager.selectedIndex.getValue() - 1)); } if (ev.key === 'ArrowDown') { ev.preventDefault(); manager.selectedIndex.setValue(Math.min(manager.selectedIndex.getValue() + 1, manager.currentSuggestions.getValue().length - 1)); } }, oninput: (ev) => { if (!manager.isOpened.getValue()) { manager.isOpened.setValue(true); } void manager.getSuggestion({ injector, term: ev.target.value }); } }, createComponent("div", { className: "input-container", style: props.style }, createComponent("div", { className: "term-icon", onclick: () => setIsOpened(true) }, props.defaultPrefix), createComponent(CommandPaletteInput, { manager: manager }), createComponent("div", { className: "post-controls" }, createComponent("div", { className: "loader-container" }, createComponent(Loader, { style: { width: '100%', height: '100%' } })), createComponent("div", { className: "close-suggestions", onclick: () => setIsOpened(false) }, createComponent(Icon, { icon: close, size: 14 })))), createComponent(CommandPaletteSuggestionList, { manager: manager, fullScreenSuggestions: props.fullScreenSuggestions }))); }, }); //# sourceMappingURL=index.js.map