mirror of
https://github.com/flarum/framework.git
synced 2025-03-15 00:05:12 +08:00
listItems
typing fix (#3176)
Co-authored-by: David Wheatley <hi@davwheat.dev>
This commit is contained in:
parent
0e00196d8e
commit
580be37eb4
@ -3,29 +3,41 @@ import Component, { ComponentAttrs } from '../Component';
|
||||
import Separator from '../components/Separator';
|
||||
import classList from '../utils/classList';
|
||||
|
||||
export interface ModdedVnodeAttrs {
|
||||
type ModdedVnodeAttrs = {
|
||||
itemClassName?: string;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export type ModdedVnode<Attrs> = Mithril.Vnode<ModdedVnodeAttrs, Component<Attrs> | {}> & {
|
||||
itemName?: string;
|
||||
itemClassName?: string;
|
||||
tag: Mithril.Vnode['tag'] & {
|
||||
isListItem?: boolean;
|
||||
isActive?: (attrs: ComponentAttrs) => boolean;
|
||||
};
|
||||
};
|
||||
|
||||
function isSeparator<Attrs>(item: ModdedVnode<Attrs>): boolean {
|
||||
return item.tag === Separator;
|
||||
type ModdedTag = Mithril.Vnode['tag'] & {
|
||||
isListItem?: boolean;
|
||||
isActive?: (attrs: ComponentAttrs) => boolean;
|
||||
};
|
||||
|
||||
type ModdedVnode = Mithril.Vnode<ModdedVnodeAttrs> & { itemName?: string; itemClassName?: string; tag: ModdedTag };
|
||||
|
||||
type ModdedChild = ModdedVnode | string | number | boolean | null | undefined;
|
||||
type ModdedChildArray = ModdedChildren[];
|
||||
type ModdedChildren = ModdedChild | ModdedChildArray;
|
||||
|
||||
/**
|
||||
* This type represents an element of a list returned by `ItemList.toArray()`,
|
||||
* coupled with some static properties used on various components.
|
||||
*/
|
||||
export type ModdedChildrenWithItemName = ModdedChildren & { itemName?: string };
|
||||
|
||||
function isVnode(item: ModdedChildren): item is Mithril.Vnode {
|
||||
return typeof item === 'object' && item !== null && 'tag' in item;
|
||||
}
|
||||
|
||||
function withoutUnnecessarySeparators<Attrs>(items: ModdedVnode<Attrs>[]): ModdedVnode<Attrs>[] {
|
||||
const newItems: ModdedVnode<Attrs>[] = [];
|
||||
let prevItem: ModdedVnode<Attrs>;
|
||||
function isSeparator(item: ModdedChildren): boolean {
|
||||
return isVnode(item) && item.tag === Separator;
|
||||
}
|
||||
|
||||
items.filter(Boolean).forEach((item: Mithril.Vnode, i: number) => {
|
||||
function withoutUnnecessarySeparators(items: ModdedChildrenWithItemName[]): ModdedChildrenWithItemName[] {
|
||||
const newItems: ModdedChildrenWithItemName[] = [];
|
||||
let prevItem: ModdedChildren;
|
||||
|
||||
items.filter(Boolean).forEach((item, i: number) => {
|
||||
if (!isSeparator(item) || (prevItem && !isSeparator(prevItem) && i !== items.length - 1)) {
|
||||
prevItem = item;
|
||||
newItems.push(item);
|
||||
@ -42,38 +54,43 @@ function withoutUnnecessarySeparators<Attrs>(items: ModdedVnode<Attrs>[]): Modde
|
||||
* By default, this tag is an `<li>` tag, but this is customisable through the
|
||||
* second function parameter, `customTag`.
|
||||
*/
|
||||
export default function listItems<Attrs extends Record<string, unknown>>(
|
||||
rawItems: ModdedVnode<Attrs> | ModdedVnode<Attrs>[],
|
||||
customTag: string | Component<Attrs> = 'li',
|
||||
export default function listItems<Attrs extends ComponentAttrs>(
|
||||
rawItems: ModdedChildrenWithItemName[],
|
||||
customTag: VnodeElementTag<Attrs> = 'li',
|
||||
attributes: Attrs = {} as Attrs
|
||||
): Mithril.Vnode[] {
|
||||
const items = rawItems instanceof Array ? rawItems : [rawItems];
|
||||
const Tag = customTag;
|
||||
|
||||
return withoutUnnecessarySeparators(items).map((item: ModdedVnode<Attrs>) => {
|
||||
const isListItem = item.tag?.isListItem;
|
||||
const active = item.tag?.isActive?.(item.attrs);
|
||||
const className = item.attrs?.itemClassName || item.itemClassName;
|
||||
return withoutUnnecessarySeparators(items).map((item) => {
|
||||
const classes = [item.itemName && `item-${item.itemName}`];
|
||||
|
||||
if (isListItem) {
|
||||
if (!isVnode(item)) {
|
||||
return (
|
||||
<Tag className={classList(classes)} {...attributes}>
|
||||
{item}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.tag.isListItem) {
|
||||
item.attrs = item.attrs || {};
|
||||
item.attrs.key = item.attrs.key || item.itemName;
|
||||
item.key = item.attrs.key;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
const node: Mithril.Vnode = isListItem ? (
|
||||
item
|
||||
) : (
|
||||
// @ts-expect-error `Component` does not have any construct or call signatures
|
||||
<Tag
|
||||
className={classList([className, item.itemName && `item-${item.itemName}`, active && 'active'])}
|
||||
key={item?.attrs?.key || item.itemName}
|
||||
{...attributes}
|
||||
>
|
||||
classes.push(item.attrs?.itemClassName || item.itemClassName);
|
||||
|
||||
if (item.tag.isActive?.(item.attrs)) {
|
||||
classes.push('active');
|
||||
}
|
||||
|
||||
return (
|
||||
<Tag className={classList(classes)} key={item?.attrs?.key || item.itemName} {...attributes}>
|
||||
{item}
|
||||
</Tag>
|
||||
);
|
||||
|
||||
return node;
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user