@furystack/shades-common-components
Version:
Common UI components for FuryStack Shades
85 lines • 4.09 kB
JavaScript
import { createComponent, Shade } from '@furystack/shades';
import { cssVariableTheme } from '../../services/css-variable-theme.js';
import { Pagination } from '../pagination.js';
import { ListItem } from './list-item.js';
let nextListId = 0;
export const List = Shade({
customElementName: 'shade-list',
css: {
display: 'block',
fontFamily: cssVariableTheme.typography.fontFamily,
width: '100%',
overflow: 'auto',
'& .shade-list-pagination': {
display: 'flex',
justifyContent: 'center',
padding: '8px 0',
},
},
render: ({ props, useDisposable, useHostProps, useRef, useState }) => {
const wrapperRef = useRef('listWrapper');
const [navSectionId] = useState('navSectionId', String(nextListId++));
useDisposable('keydown-handler', () => {
const listener = (ev) => {
props.listService.handleKeyDown(ev);
if (ev.key === 'Enter' && props.listService.hasFocus.getValue()) {
const focusedItem = props.listService.focusedItem.getValue();
if (focusedItem && props.onItemActivate) {
props.onItemActivate(focusedItem);
}
}
};
window.addEventListener('keydown', listener, true);
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener, true) };
});
const { pagination } = props;
let visibleItems;
let pageCount = 1;
if (pagination) {
const { itemsPerPage, page } = pagination;
pageCount = Math.ceil(props.items.length / itemsPerPage);
const startIndex = (page - 1) * itemsPerPage;
visibleItems = props.items.slice(startIndex, startIndex + itemsPerPage);
}
else {
visibleItems = props.items;
}
props.listService.items.setValue(visibleItems);
useDisposable('focus-coordination', () => {
const handleFocusOut = (ev) => {
const wrapper = wrapperRef.current;
if (wrapper && (!ev.relatedTarget || !wrapper.contains(ev.relatedTarget))) {
props.listService.hasFocus.setValue(false);
}
};
queueMicrotask(() => {
const wrapper = wrapperRef.current;
if (wrapper) {
wrapper.addEventListener('focusout', handleFocusOut);
}
});
return {
[Symbol.dispose]: () => {
const wrapper = wrapperRef.current;
if (wrapper) {
wrapper.removeEventListener('focusout', handleFocusOut);
}
},
};
});
if (props.onSelectionChange) {
const { onSelectionChange } = props;
useDisposable('selectionChangeCallback', () => props.listService.selection.subscribe((newSelection) => {
onSelectionChange(newSelection);
}));
}
useHostProps({
'data-variant': props.variant || undefined,
});
return (createComponent(createComponent, null,
createComponent("div", { ref: wrapperRef, role: "listbox", ariaMultiSelectable: "true", className: "shade-list-wrapper", "data-nav-section": props.navSection ?? `list-${navSectionId}`, onclick: () => props.listService.hasFocus.setValue(true) }, visibleItems.map((item) => (createComponent(ListItem, { item: item, listService: props.listService, renderItem: props.renderItem, renderIcon: props.renderIcon, renderSecondaryActions: props.renderSecondaryActions, onActivate: props.onItemActivate })))),
pagination && pageCount > 1 && (createComponent("div", { className: "shade-list-pagination" },
createComponent(Pagination, { count: pageCount, page: pagination.page, onPageChange: pagination.onPageChange, size: "small" })))));
},
});
//# sourceMappingURL=list.js.map