mirror of
https://github.com/rio-labs/rio.git
synced 2026-01-05 04:39:49 -06:00
141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
import { componentsByElement } from '../componentManagement';
|
|
import { ComponentId } from '../dataModels';
|
|
import { ComponentBase } from './componentBase';
|
|
import { CustomListItemComponent } from './customListItem';
|
|
import { HeadingListItemComponent } from './headingListItem';
|
|
import { ColumnComponent, LinearContainerState } from './linearContainers';
|
|
import { SeparatorListItemComponent } from './separatorListItem';
|
|
|
|
export class ListViewComponent extends ColumnComponent {
|
|
constructor(id: ComponentId, state: Required<LinearContainerState>) {
|
|
state.spacing = 0;
|
|
state.proportions = null;
|
|
super(id, state);
|
|
}
|
|
|
|
createElement(): HTMLElement {
|
|
let element = super.createElement();
|
|
element.classList.add('rio-list-view');
|
|
return element;
|
|
}
|
|
|
|
updateElement(
|
|
deltaState: LinearContainerState,
|
|
latentComponents: Set<ComponentBase>
|
|
): void {
|
|
super.updateElement(deltaState, latentComponents);
|
|
|
|
// Columns don't wrap their children in divs, but ListView does. Hence
|
|
// the overridden updateElement.
|
|
this.replaceChildren(
|
|
latentComponents,
|
|
deltaState.children,
|
|
this.element,
|
|
true
|
|
);
|
|
|
|
// Clear everybody's position
|
|
for (let child of this.element.children) {
|
|
let element = child.firstElementChild as HTMLElement;
|
|
element.style.left = '0';
|
|
element.style.top = '0';
|
|
}
|
|
|
|
// Update the styles of the children
|
|
this._updateChildStyles();
|
|
|
|
// Update the layout
|
|
this.makeLayoutDirty();
|
|
}
|
|
|
|
_isGroupedListItemWorker(comp: ComponentBase): boolean {
|
|
// Is this a recognized list item type?
|
|
if (
|
|
comp instanceof HeadingListItemComponent ||
|
|
comp instanceof SeparatorListItemComponent
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if (comp instanceof CustomListItemComponent) {
|
|
return true;
|
|
}
|
|
|
|
// If the component only has a single child, drill down
|
|
if (comp.children.size === 1) {
|
|
return this._isGroupedListItemWorker(
|
|
comp.children.values().next().value
|
|
);
|
|
}
|
|
|
|
// Everything else defaults to being grouped
|
|
return true;
|
|
}
|
|
|
|
_isGroupedListItem(elem: HTMLElement): boolean {
|
|
let comp = componentsByElement.get(
|
|
elem.firstElementChild as HTMLElement
|
|
);
|
|
|
|
if (comp === undefined) {
|
|
throw new Error(`Cannot find component for list element ${elem}`);
|
|
}
|
|
|
|
return this._isGroupedListItemWorker(comp);
|
|
}
|
|
|
|
_updateChildStyles(): void {
|
|
// Precompute which children are grouped
|
|
let groupedChildren = new Set<any>();
|
|
for (let child of this.element.children) {
|
|
let castChild = child as HTMLElement;
|
|
|
|
if (this._isGroupedListItem(castChild)) {
|
|
groupedChildren.add(castChild);
|
|
castChild.classList.add('rio-listview-grouped');
|
|
} else {
|
|
castChild.classList.remove('rio-listview-grouped');
|
|
}
|
|
}
|
|
|
|
// Round the corners of each first & last child in a a group, and add
|
|
// separators between them.
|
|
//
|
|
// Make sure to work on a copy because the element will be modified by
|
|
// the loop.
|
|
for (let curChildUncast of Array.from(this.element.children)) {
|
|
let curChild = curChildUncast as HTMLElement;
|
|
|
|
// Is this even a regular list item?
|
|
let curIsGrouped = groupedChildren.has(curChild);
|
|
|
|
// Look up the neighboring elements
|
|
let prevIsGrouped = groupedChildren.has(
|
|
curChild.previousElementSibling
|
|
);
|
|
let nextIsGrouped = groupedChildren.has(
|
|
curChild.nextElementSibling
|
|
);
|
|
|
|
if (!curIsGrouped) {
|
|
continue;
|
|
}
|
|
|
|
// Round the corners
|
|
let topRadius = prevIsGrouped
|
|
? '0'
|
|
: 'var(--rio-global-corner-radius-medium)';
|
|
let bottomRadius = nextIsGrouped
|
|
? '0'
|
|
: 'var(--rio-global-corner-radius-medium)';
|
|
|
|
curChild.style.borderTopLeftRadius = topRadius;
|
|
curChild.style.borderTopRightRadius = topRadius;
|
|
curChild.style.borderBottomLeftRadius = bottomRadius;
|
|
curChild.style.borderBottomRightRadius = bottomRadius;
|
|
|
|
curChild.style.overflow = 'hidden';
|
|
}
|
|
}
|
|
}
|