mirror of
https://github.com/flarum/framework.git
synced 2025-01-28 03:35:49 +08:00
feat: allow use of any tag in listItems
helper (#3147)
* feat: allow use of any tag in `listItems` helper * fix: fix missing optional chaining * chore: use more optional chaining * fix: various typings errors * chore: replace `Vnode[]` with `Children`
This commit is contained in:
parent
029e34bfd7
commit
0db7f3be74
|
@ -1,14 +1,15 @@
|
||||||
import type Mithril from 'mithril';
|
import type Mithril from 'mithril';
|
||||||
import Separator from '../components/Separator';
|
import Separator from '../components/Separator';
|
||||||
import classList from '../utils/classList';
|
import classList from '../utils/classList';
|
||||||
|
import type * as Component from '../Component';
|
||||||
|
|
||||||
function isSeparator(item): boolean {
|
function isSeparator(item: Mithril.Children): boolean {
|
||||||
return item.tag === Separator;
|
return item.tag === Separator;
|
||||||
}
|
}
|
||||||
|
|
||||||
function withoutUnnecessarySeparators(items: Array<Mithril.Vnode>): Array<Mithril.Vnode> {
|
function withoutUnnecessarySeparators(items: Mithril.Children): Mithril.Children {
|
||||||
const newItems = [];
|
const newItems: Mithril.Children = [];
|
||||||
let prevItem;
|
let prevItem: Mithril.Child;
|
||||||
|
|
||||||
items.filter(Boolean).forEach((item: Mithril.Vnode, i: number) => {
|
items.filter(Boolean).forEach((item: Mithril.Vnode, i: number) => {
|
||||||
if (!isSeparator(item) || (prevItem && !isSeparator(prevItem) && i !== items.length - 1)) {
|
if (!isSeparator(item) || (prevItem && !isSeparator(prevItem) && i !== items.length - 1)) {
|
||||||
|
@ -20,17 +21,36 @@ function withoutUnnecessarySeparators(items: Array<Mithril.Vnode>): Array<Mithri
|
||||||
return newItems;
|
return newItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ModdedVnodeAttrs {
|
||||||
|
itemClassName?: string;
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModdedVnode<Attrs> = Mithril.Vnode<ModdedVnodeAttrs, Component.default<Attrs> | {}> & {
|
||||||
|
itemName?: string;
|
||||||
|
itemClassName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `listItems` helper wraps a collection of components in <li> tags,
|
* The `listItems` helper wraps an array of components in the provided tag,
|
||||||
* stripping out any unnecessary `Separator` components.
|
* stripping out any unnecessary `Separator` components.
|
||||||
|
*
|
||||||
|
* By default, this tag is an `<li>` tag, but this is customisable through the
|
||||||
|
* second function parameter, `customTag`.
|
||||||
*/
|
*/
|
||||||
export default function listItems(items: Mithril.Vnode | Array<Mithril.Vnode>): Array<Mithril.Vnode> {
|
export default function listItems<Attrs extends Record<string, unknown>>(
|
||||||
|
items: ModdedVnode<Attrs> | ModdedVnode<Attrs>[],
|
||||||
|
customTag: string | Component.default<Attrs> = 'li',
|
||||||
|
attributes: Attrs = {}
|
||||||
|
): Mithril.Vnode[] {
|
||||||
if (!(items instanceof Array)) items = [items];
|
if (!(items instanceof Array)) items = [items];
|
||||||
|
|
||||||
return withoutUnnecessarySeparators(items).map((item: Mithril.Vnode) => {
|
const Tag = customTag;
|
||||||
const isListItem = item.tag && item.tag.isListItem;
|
|
||||||
const active = item.tag && item.tag.isActive && item.tag.isActive(item.attrs);
|
return withoutUnnecessarySeparators(items).map((item: ModdedVnode<Attrs>) => {
|
||||||
const className = (item.attrs && item.attrs.itemClassName) || item.itemClassName;
|
const isListItem = item.tag?.isListItem;
|
||||||
|
const active = item.tag?.isActive?.(item.attrs);
|
||||||
|
const className = item.attrs?.itemClassName || item.itemClassName;
|
||||||
|
|
||||||
if (isListItem) {
|
if (isListItem) {
|
||||||
item.attrs = item.attrs || {};
|
item.attrs = item.attrs || {};
|
||||||
|
@ -41,12 +61,14 @@ export default function listItems(items: Mithril.Vnode | Array<Mithril.Vnode>):
|
||||||
const node: Mithril.Vnode = isListItem ? (
|
const node: Mithril.Vnode = isListItem ? (
|
||||||
item
|
item
|
||||||
) : (
|
) : (
|
||||||
<li
|
// @ts-expect-error `Component` does not have any construct or call signatures
|
||||||
|
<Tag
|
||||||
className={classList([className, item.itemName && `item-${item.itemName}`, active && 'active'])}
|
className={classList([className, item.itemName && `item-${item.itemName}`, active && 'active'])}
|
||||||
key={(item.attrs && item.attrs.key) || item.itemName}
|
key={item?.attrs?.key || item.itemName}
|
||||||
|
{...attributes}
|
||||||
>
|
>
|
||||||
{item}
|
{item}
|
||||||
</li>
|
</Tag>
|
||||||
);
|
);
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user