Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ The passed array and its contents will be left unmodified. Any fragment nodes wi
* [Example - Inserting between children](/examples.html#inserting-between-children)
* [Guide - Inserting new nodes](/guide/inserting-new-nodes.html)

## countChildren() <Badge text="0.3+" />

### Type

```ts
function countChildren(
children: VNodeArrayChildren,
options: IterationOptions = ALL_VNODES
): number
```

### Description

Counts the number of 'top-level' VNodes. The children of a fragment will be considered 'top-level' nodes rather than the fragment itself.

The [`options`](#iterationoptions) object can be used to decide which node types should be counted. If no options object is passed then all nodes will be counted. If an `options` object is passed, all nodes will be skipped by default unless explicitly ruled in.

### See also

* [Guide - Other helpers](/guide/other-helpers.html)

## eachChild()

### Type
Expand Down
3 changes: 2 additions & 1 deletion packages/docs/src/guide/iterators.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ export default function ChildComponent(_, { slots }) {

See it on the SFC Playground: [Composition API](https://play.vuejs.org/#eNp9VGuP0zAQ/CurgNRWtEl5SEjhjgNOJ94PcSfxgSDIJU5r6tiR7fSKqv53xnaaPrhDrdTaO56dXe94Hb1smnjZsiiNTkyheWPJMNs2zzPJ60ZpS2vSLC8sXzLaUKVVTQPgB338Azf2G7fzK2Vz0QHi5GA3/m2Az2ShpLHELasNnfa0w++ZJKSxbGVTGnxl5WBMZq5uUrK6RdbxQfy1Zkz2iCoX5h/IKwGBhxyZ/DHaSXCRl0JAxHBEp89p7Y5XStNwJ5FUFaSOQpj8KnZHcc6xul0Q43uShN6ha1gA1ojcMqyITq5ba5WkF4XgxeI0i7rcWeTDRJeOMMeGAycBHU4K9HBygyZOrOtih+/paTmBZDB6tVwGtT2vJwCGVx3ES98Lo2HrUJNrG21Qxzbwj+j90n3LwfOGl+xAsD+ZCL4Vmhw2ApHjeg4g0TgKIzWp8wYjoySG0rce+X0AxaXby8iiF2bBtRUswThOllKVbNJaLjwoi+bWNiZNklY2i1lcqDq5C5+U0HW8GTNTT661ujFMxyVbQk8WbS8cSo/nG1J7v8xvN4ozUtkW7HzORamZHNPl+7dffp5//vjx4tPVZX/oLp3eQmzluUpW5a2wVLUSHsJNedJzhUySSTv8OUY6I5Q1tOnmN0x20SXHPfowavNMZzGccHZG33/4hu/wqpXWm3Vf+nBLA5O1tXOQ+3lAD8c0PSoLrgt8Gs+KljQfDkq+hDu962m3/uXJQ8KU7q/9n82vkbe2k+OEnDl8KwDfKhhRSrIN9nEWx/3geoz9I5iJC+MuphvmB4REoRfXSpdMp/SwWZFRgpd0bzqdPnOhOtczLjGhDcLTZtVtrjC4pZ2n9Gi63WzysuRy1sOQOYOSLkNeLGYamsuU7lVP3ccfuiVzURR7mQMdTcnJ8ZRwsac8yBfCqNQaXFTFZ0eGwcA3XDD9uXHjcWgcPDbq5p3fc89Y12CcmbNiccv+b7MKnvqiGeywhPn7mIVoZkP44vITHpK9YK3KVgD9n+BXhi5gvJUMsFfoGGTv4bzat95CqP7KXKwsk2ZbVP8Oe3wWwTPOBXeVvpP7OH7S23nzF2B7Vmo=) | [Options API](https://play.vuejs.org/#eNp9Vftv0zAQ/ldOAamdaJPykJDCxoBp4v0Qm8QPBEEWO42pa0f2pSuq+r9zfiRry4Y2qfXdd9/d+e5zN8nLtk1XHU/y5NhWRrT4vFBi2WqD8EFY/CawudRYSqiNXsIozfas6W87KhRAofjaxzBel51E2DhrpYlIcYU2DwbY53Sm7STEA7ASy/FRDzQcO6P6E4BAviSa7/0ZYAPI15jD6CtnownYRl/ngKbjkfMA9dpwrgZcXUp7B/CV7PgBX4/6Eb74s4t1n0uOjWY3Hbq4l1L2nRRYYK0NjCutLPo2QNeAjbCp72nouO8ydQxw4lP3nlhBSFwo+j/OhnHRgcJaWSKnE8DxVYeoFbyopKgWJ0USSyoS7wa4cAlKMjhwFtAhUtJ4ptc0nym6AUX8QA+rKfVCjL4NocJUBl5PQBhRR4hvZcdNt7wJPbq7hu1wsbcUvXsVflrE80Ywvlewj8yk6AvN9i+CPIf97EGSSRJ2fbosW9plrUgHYWzRQc0Nky2SF3YhDEqekWKmK6UZn3YopAcVSYPY2jzLOtUu5iktf3YXPmNU16Ex5XY5vTL62nKTMr6ieoqkHzhVeig8KjUKdQMNbKNAiZQUOTgMZ13FzxohmeFqAhfv3375efb548fzT5cXQ9BddRLTP8quO1WhoEl50rNe4uOfE0pnpUYL27jTYeWrmJzm6N3Um2c6TUkjp6fw3atqB687hQTeL33c05AyuyWcPPcfD+DhBGYHbR31fPENacYjJlYk6fh4DOdfnjwkzOH+xn/Z/jqKr0Io5NThO0nwvoIjyEF1QT4/KBnNh8Zj8Y/kNq2sG0xc5gdAicJdXGnDuMnhYbsGq6VgcG82mz3zL0hp5kLRhrbknrXraFzT4jJscng0641tyZhQ8wFGmQuqJGYoq8XcUM0sh3v1U/fng27JXFXVTuZABzNw5XhKUrGn3MsX3NQpWhpULeYHgnGvvZDcfG7deuwLhx4bff3O29yzFi+YYhpeLW6x/7broKkvhpMcViT+wYdUNMfgPr/4RA/JjnOpWScJ/R/nV063QOutVYC9ohujsndwvtq3XkLU/aU9XyNXtm+qf5fjL1dCmnEquKv1m3Ifp08GOW//AlHHcjQ=)

For the specific case of counting children, the dedicated [`countChildren()`](./other-helpers#counting-children) helper could be used instead.

While these examples need to display the count, a more common scenario involves only needing to know whether the count is 0. The [`isEmpty()`](/api.html#isempty) helper can be used in that case.
While these examples need to display the count, a more common scenario involves only needing to know whether the count is 0. The [`isEmpty()`](./other-helpers#checking-for-an-empty-slot) helper can be used in that case.

It is worth noting that the count here is just a count of the VNodes. It is not necessarily an accurate count of the number of `<li>` elements. If any of the children had been a component it would have added 1 to the count, even though a component wouldn't necessarily render exactly one `<li>` element.
27 changes: 27 additions & 0 deletions packages/docs/src/guide/other-helpers.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Other helpers

## Counting children

A helper for counting the children.

This uses the same iteration logic as the various [iterators](./iterators). Fragments are not counted directly, with the children of any fragments being counted instead.

The second argument of `countChildren()` is optional and allows [iteration options](/api.html#iterationoptions) to be specified, controlling the types of VNodes that are counted.

```js
import { h } from 'vue'
import { countChildren, SKIP_COMMENTS } from '@skirtle/vue-vnode-utils'

function ChildComponent(_, { slots }) {
const children = slots.default?.() ?? []
const count = countChildren(children, SKIP_COMMENTS)

return h('div', [
h('div', `Child count: ${count}`),
count ? h('ul', children) : null
])
}
```

See it on the SFC Playground: [Composition API](https://play.vuejs.org/#eNp9VGtv0zAU/StXAamtaJPykJDCxoBp4v0Qm8QHgiBLnNara0e20xVV/e8c22nWdBvaPjS+55577vU93kSv6zpeNSxKoyNTaF5bMsw29ctM8mWttKUNaZYXlq8YbanSakkD4Add/BM39ge38wtlc9EC4qR3Gl8Z4DNZKGksccuWho472uHPTBLKWLa2KQ2+s3IwJjNX1ylZ3aDquBd/qxmTHaLKhbkFeSMgsM+RyV+jGwku8loIiBiO6PglbVx6pTQNbySSqoLUUQiT/4pdKvIcqzsFMf6PkjA7TA0fgNUitwxfREeXjbVK0qtC8GJxnEVt7SzyYaJzR5jjwIGTgA6ZAjOcXGOIE+um2OI7elpNIBmMXi2XQW3H6wmA4VUL8dL3whjYJvTkxkZb9LEL3BK937ofOXje8ZL1BPvMRPCd0KQ/CEQO++lBonEUVmqyzGusjJJYSj961PcBNJfuLiOLXpkF11awBOs4WUlVskljufCgLJpbW5s0SRpZL2ZxoZbJffikhK7Dw5iZ5eRSq2vDdFyyFfRk0e7CofRwvyG188v8bqNsqFCNtKdzLkrN5JjOP77/9vv06+fPZ18uzruc+2R6B7G1pypZlTfCUtVIWAgX5UlPFQpJJu3w9xjVjFDW0LZd37DYRVsc1+jDaM0zncQwwskJ/fy1h3VqAeypHu4YDuR7cxE8bRstaT4clHwFC3pr0833H88TKFN6uPE/tn9G3r+usCt54vCNAHxXbEQpySZ4xPkYl4A7MPavYCYujJt+u7GPCIVCx5dKl0yn9Lhek1GCl/RgOp2+cKFlrmdcYg1rhKf1uj1cYztLO0/pyXR3WOdlyeWsg6FyBiVthbxYzDQ0lyk9qJ67P590R+WiKPYqBzqakpPjKWFVT9mrF8Lo1BpcScVnB67AVtdcMP21dkvQdwdeFHX9wZ+5t6odMHLmrFjccX5l1sE43zTDzq/g8C5mIZrZED47/4LXYi+4VGUjgP5P8DvDFLDESgbYG0wMsvdwXu177xN0f2HO1pZJs2uqe2w9PovgDLfr97V+I/dp/Kzz7PYf9mdPuQ==) | [Options API](https://play.vuejs.org/#eNp9Vftv0zAQ/ldOAamdaJPykJDCYMA08X6ITeIHgiCLncara0f2pSuq+r9zfiRry4Y2qfXdd9/d+e5zN8mrtk1XHU/y5NhWRrT4olBi2WqD8FFY/C6wudBYSqiNXsIozfas6ZUdFQqgUHztYxivy04ibJy10kSkuEKbBwPsczrTdhLiAViJ5fioBxqOnVH9CUAgXxLNj/4MsAHka8xh9I2z0QRso69zQNPxyHmAemM4VwOuLqW9A/hadvyAr0f9DF/82cW6zyXHRrObDl3cKyn7TgossNYGxpVWFn0boGvARtjU9zR03HeZOgZ47lP3nlhBSFwo+j/OhnHRgcJaWSKnE8DxZYeoFbyspKgWz4skllQk3g1w7hKUZHDgLKBDpKTxTK9pPlN0A4r4gR5WU+qFGH0bQoWpDLyegDCijhDfyo6bbnkTenR3DdvhYm8pevcq/LSI561gfK9gH5lJ0Rea7V8EeQ772YMkkyTs+nRZtrTLWpEOwtiig5obJlskL+1CGJQ8I8VMV0ozPu1QSA8qkgaxtXmWdapdzFNa/uwufMaorkNjyu1yemn0teUmZXxF9RRJP3Cq9FB4VGoU6gYa2EaBEikpcnBUulN42gjJDFcTOP/w7uuv0y+fPp19vjgfYu4qk4j+EXbdqQoFDcqTnvYKH/+aUDYrNVrYxpUOG1/F5DRG76bWPNNJShI5OYEfXlQR66ol4F7V457hoPyjoL/4UDTjERMr0m18IYbzb88TKHO4v/Fftr+PovRDyhOH7yTB+2RHkIPqgkZ+UioaAs3A4h/JbVpZd/txYx8AJQodX2rDuMnhYbsGq6VgcG82mz3zz0Rp5kLRGrbknrXraFzTdjJscng0641tyZhQ8wFGmQuqJGYoq8XcUM0sh3v1U/fng27JXFXVTuZABzNw5XhKkqqn3MsX3NQpWhpJLeYHqnBPupDcfGndEuyrg14Uff3e29zbFS+YYhpeLW6xX9l1EM5Xw2nnV6TwwYdUNMfgPjv/TK/FjnOpWScJ/R/nN063QEusVYC9phujsndwvtp3XifU/YU9WyNXtm+qf3zjz1NCynC7flfrN+U+Tp8Mmt3+Bfk4a4M=)

If you need more fine-grained control over the counting you can use [`eachChild()` or `reduceChildren()`](./iterators) instead.

## Extracting a single child

Slots return an array of children, but some components only allow for one child in their slot, like Vue's built-in components `<KeepAlive>` and `<Transition>`. `extractSingleChild()` can be used to pull out a meaningful root node from a slot. It skips over fragments, comments and text nodes, trying to find a component or element node.
Expand Down
27 changes: 27 additions & 0 deletions packages/vue-vnode-utils/src/__tests__/iterators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
ALL_VNODES,
betweenChildren,
COMPONENTS_AND_ELEMENTS,
countChildren,
eachChild,
everyChild,
extractSingleChild,
Expand Down Expand Up @@ -1615,3 +1616,29 @@ describe('extractSingleChild', () => {
expect(out).toBe(node)
})
})

describe('countChildren', () => {
it('countChildren - 129c', () => {
expect(countChildren([])).toBe(0)
expect(countChildren([''])).toBe(1)
expect(countChildren(['Text'])).toBe(1)
expect(countChildren(['Text', 'Text'])).toBe(2)
expect(countChildren([['Text', 'Text', ['Text']], 'Text'])).toBe(4)
expect(countChildren([null])).toBe(1)
expect(countChildren([false])).toBe(1)
expect(countChildren([true])).toBe(1)
expect(countChildren([h('div')])).toBe(1)
expect(countChildren([h('div', {}, [h('span')])])).toBe(1)
expect(countChildren([h({ template: 'abc' })])).toBe(1)
})

it('countChildren - 9294', () => {
const children = [[], ['Text'], '', h('div'), h('span'), null, h({ template: 'abc' })]

expect(countChildren(children)).toBe(6)
expect(countChildren(children, ALL_VNODES)).toBe(6)
expect(countChildren(children, COMPONENTS_AND_ELEMENTS)).toBe(3)
expect(countChildren(children, SKIP_COMMENTS)).toBe(5)
expect(countChildren(children, {})).toBe(0)
})
})
1 change: 1 addition & 0 deletions packages/vue-vnode-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export {
ALL_VNODES,
betweenChildren,
COMPONENTS_AND_ELEMENTS,
countChildren,
eachChild,
everyChild,
extractSingleChild,
Expand Down
17 changes: 17 additions & 0 deletions packages/vue-vnode-utils/src/iterators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,3 +429,20 @@ export const extractSingleChild = (children: VNodeArrayChildren): VNode | undefi

return node
}

export const countChildren = (
children: VNodeArrayChildren,
options: IterationOptions = ALL_VNODES
) => {
if (__DEV__) {
checkArguments('count', [children, options], ['array', 'object'])
}

let count = 0

someChildInternal(children, () => {
++count
}, options)

return count
}
Loading