UNPKG

@furystack/shades-common-components

Version:

Common UI components for FuryStack Shades

85 lines 4.09 kB
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