@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
96 lines • 4.85 kB
JavaScript
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