Skip to content

Commit bad86b7

Browse files
committed
Rename 'childProp' to 'childSource' and fix a few more render bugs
Also finishes out the list view documentation.
1 parent b5a45fa commit bad86b7

3 files changed

Lines changed: 116 additions & 28 deletions

File tree

docs/docs/code-views/list-view.md

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,78 @@ return function View() {
170170

171171
If you group multiple times, you can specify a separate rendering for each grouping level by passing an array of grouping configurations to `groupings`.
172172

173-
### Heirarchies / Children (`childProp` / `maxChildDepth`)
173+
### Sublists (`childSource` / `maxChildDepth`)
174+
175+
Lists can recursively contain sublists to create full heirarchies of entries. By default, datacore
176+
will look for the `$children` and `children` properties on rows to determine sublists to render.
177+
For example, this will produce a nested list of two top level items (`Hello` and `Goodbye`), each with three subitems.
178+
179+
```jsx
180+
const DATA = [
181+
{
182+
title: "Hello",
183+
children: [
184+
{ title: "One" },
185+
{ title: "Two" },
186+
{ title: "Three" }
187+
]
188+
},
189+
{
190+
title: "Goodbye",
191+
children: [
192+
{ title: "Four" },
193+
{ title: "Five" },
194+
{ title: "Six" }
195+
]
196+
}
197+
];
198+
199+
return function View() {
200+
return <dc.List rows={DATA} renderer={item => item.title} />;
201+
}
202+
```
203+
204+
If you want to use another field instead, you can override the `childSource` property to provide
205+
either a different property, list of properties, or even an arbitrary function:
206+
207+
```jsx
208+
return function View() {
209+
// Provide an alternative property to use.
210+
return <dc.List rows={...} childSource={"items"} ... />;
211+
// Provide a list of alternative properties.
212+
return <dc.List rows={...} childSource={["items", "things"]} ... />;
213+
// Provide an arbitrary function.
214+
return <dc.List rows={...} childSource={item => item.value("doodads")} ... />;
215+
}
216+
```
217+
218+
You can also control the maximum depth of children to show via `maxChildDepth`; this defaults to
219+
a small constant (<20) by default.
220+
221+
```jsx
222+
return function View() {
223+
// Show only at most two levels of children.
224+
return <dc.List rows={...} childSource={"items"} maxChildDepth={2} ... />;
225+
}
226+
```
227+
228+
Children and grouping can be combined to create very interesting views, such as this one which
229+
dynamically generates a list of books as well as all pages/sections immediately linking to that book:
230+
231+
```jsx
232+
// Finds all things linked to book that themselves can be linked to.
233+
function findLinked(book) {
234+
return dc.query(`[[${book.$path}]] and $types.econtains("linkable")`);
235+
}
236+
237+
return function View() {
238+
// Groups both by
239+
const books = dc.useQuery("@page and #book");
240+
const groupedBooks = dc.useArray(books, array => array.groupBy(book => book.value("genre") ?? "No Genre"));
241+
242+
return <dc.List rows={groupedBooks} renderer={(book) => book.$link} maxChildDepth={1} childSource={findLinked} />;
243+
}
244+
```
174245
175246
## Full Reference
176247
@@ -216,7 +287,7 @@ export interface ListState<T> {
216287
*
217288
* If null, child extraction is disabled and no children will be fetched. If undefined, uses the default.
218289
*/
219-
childProp?: null | string | string[] | ((row: T) => T[]);
290+
childSource?: null | string | string[] | ((row: T) => T[]);
220291
}
221292

222293
export interface GroupingConfig<T> {

src/api/local-api.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,26 @@ export class DatacoreLocalApi {
166166
return this.api.tryEvaluate(expression, variables, sourcePath ?? this.path);
167167
}
168168

169+
/** Execute a textual or typed index query, returning all results. */
170+
public query(query: string | IndexQuery): Indexable[] {
171+
return this.api.query(query);
172+
}
173+
174+
/** Execute a textual or typed index query, returning all results. */
175+
public tryQuery(query: string | IndexQuery): Result<Indexable[], string> {
176+
return this.api.tryQuery(query);
177+
}
178+
179+
/** Execute a textual or typed index query, returning results plus performance metadata. */
180+
public fullquery(query: string | IndexQuery): SearchResult<Indexable> {
181+
return this.api.fullquery(query);
182+
}
183+
184+
/** Execute a textual or typed index query, returning results plus performance metadata. */
185+
public tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string> {
186+
return this.api.tryFullQuery(query);
187+
}
188+
169189
/////////////
170190
// Hooks //
171191
/////////////

src/api/ui/views/list.tsx

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export interface ListState<T> {
5858
*
5959
* If null, child extraction is disabled and no children will be fetched. If undefined, uses the default.
6060
*/
61-
childProp?: null | string | string[] | ((row: T) => T[]);
61+
childSource?: null | string | string[] | ((row: T) => T[]);
6262
}
6363

6464
/**
@@ -104,7 +104,7 @@ export function ListView<T>(props: ListState<T>) {
104104

105105
// Maximum amount of recursion we'll allow when expanding children.
106106
const maxChildDepth = useMemo(() => props.maxChildDepth ?? 12, [props.maxChildDepth]);
107-
const childFunc = useMemo(() => ensureChildrenFunc(props.childProp), [props.childProp]);
107+
const childFunc = useMemo(() => ensureChildrenFunc(props.childSource), [props.childSource]);
108108

109109
return (
110110
<div ref={containerRef} className="datacore-list">
@@ -165,23 +165,14 @@ function ListGroup<T>({
165165
);
166166
} else {
167167
// This is a leaf cluster. Render it directly.
168-
if (type === "ordered") {
168+
if (type === "ordered" || type === "unordered") {
169169
return (
170170
<HtmlList<T>
171-
type="ordered"
171+
type={type}
172172
rows={rows}
173173
renderer={renderer}
174-
maxChildDepth={maxChildDepth}
175-
childFunc={childFunc}
176-
/>
177-
);
178-
} else if (type === "unordered") {
179-
return (
180-
<HtmlList<T>
181-
type="unordered"
182-
rows={rows}
183-
renderer={renderer}
184-
maxChildDepth={maxChildDepth}
174+
maxDepth={maxChildDepth}
175+
depth={0}
185176
childFunc={childFunc}
186177
/>
187178
);
@@ -222,25 +213,28 @@ function HtmlList<T>({
222213
type,
223214
rows,
224215
renderer,
225-
maxChildDepth,
216+
maxDepth,
217+
depth,
226218
childFunc,
227219
}: {
228220
type: "ordered" | "unordered";
229221
rows: T[];
230222
renderer: (row: T) => React.ReactNode | Literal;
231-
maxChildDepth: number;
223+
maxDepth: number;
224+
depth: number;
232225
childFunc: (element: T) => T[];
233226
}) {
234227
if (type === "ordered") {
235228
return (
236229
<ol className={"datacore-list datacore-list-ordered"}>
237230
{rows.map((element, index) => (
238231
<HtmlListItem<T>
232+
type={type}
239233
element={element}
240234
key={index}
241235
renderer={renderer}
242-
maxDepth={maxChildDepth}
243-
depth={0}
236+
maxDepth={maxDepth}
237+
depth={depth}
244238
childFunc={childFunc}
245239
/>
246240
))}
@@ -251,11 +245,12 @@ function HtmlList<T>({
251245
<ul className={"datacore-list datacore-list-unordered"}>
252246
{rows.map((element, index) => (
253247
<HtmlListItem<T>
248+
type={type}
254249
element={element}
255250
key={index}
256251
renderer={renderer}
257-
maxDepth={maxChildDepth}
258-
depth={0}
252+
maxDepth={maxDepth}
253+
depth={depth}
259254
childFunc={childFunc}
260255
/>
261256
))}
@@ -266,12 +261,14 @@ function HtmlList<T>({
266261

267262
/** A single <li> item (potentially with children) in an HTML list. */
268263
function HtmlListItem<T>({
264+
type,
269265
element,
270266
renderer,
271267
maxDepth,
272268
depth,
273269
childFunc,
274270
}: {
271+
type: "ordered" | "unordered";
275272
element: T;
276273
renderer: (row: T) => React.ReactNode | Literal;
277274
maxDepth: number;
@@ -286,16 +283,16 @@ function HtmlListItem<T>({
286283
return (
287284
<li className={"datacore-list-item"}>
288285
{ensureListElement(renderer(element))}
289-
{children?.map((child, index) => (
290-
<HtmlListItem<T>
291-
element={child}
292-
key={index}
286+
{children && (
287+
<HtmlList<T>
288+
type={type}
289+
rows={children}
293290
maxDepth={maxDepth}
294291
depth={depth + 1}
295292
renderer={renderer}
296293
childFunc={childFunc}
297294
/>
298-
))}
295+
)}
299296
</li>
300297
);
301298
}

0 commit comments

Comments
 (0)