forgebook 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -5
- package/dist/bin/forgebook/agentLoopCommands.mjs +3 -0
- package/dist/bin/forgebook/agentText.d.ts +2 -2
- package/dist/bin/forgebook/agentText.js +15 -13
- package/dist/bin/forgebook/agents.md +21 -6
- package/dist/bin/forgebook/mcpTools.js +1 -1
- package/dist/bin/forgebook/newStoriesFile.js +7 -2
- package/dist/bin/forgebook-docs/collectDocs.js +1 -1
- package/dist/src/argsProps.d.ts +32 -0
- package/dist/src/argsProps.js +69 -0
- package/dist/src/catalogue.d.ts +48 -5
- package/dist/src/catalogue.js +47 -8
- package/dist/src/frame/mountFrame.js +26 -7
- package/dist/src/frame/touchCaret.d.ts +43 -0
- package/dist/src/frame/touchCaret.js +195 -0
- package/dist/src/frame/touchEvents.d.ts +3 -2
- package/dist/src/frame/touchEvents.js +17 -18
- package/dist/src/frame/touchLayer.js +48 -7
- package/dist/src/propGroups.d.ts +66 -3
- package/dist/src/propGroups.js +67 -13
- package/dist/src/shell/ApiReference.d.ts +9 -4
- package/dist/src/shell/ApiReference.js +47 -39
- package/dist/src/shell/AudienceSections.js +1 -1
- package/dist/src/shell/ComponentPage.d.ts +5 -2
- package/dist/src/shell/ComponentPage.js +4 -18
- package/dist/src/shell/DocBlocks.js +1 -1
- package/dist/src/shell/PropRows.d.ts +42 -0
- package/dist/src/shell/PropRows.js +60 -0
- package/dist/src/shell/PropsTab.d.ts +14 -10
- package/dist/src/shell/PropsTab.js +18 -58
- package/dist/src/shell/RequiredFirstSwitch.d.ts +16 -0
- package/dist/src/shell/RequiredFirstSwitch.js +8 -0
- package/dist/src/shell/Stage.js +11 -23
- package/dist/src/shell/StoryCoverage.js +4 -1
- package/dist/src/shell/docPage.css +6 -0
- package/dist/src/shell/dockTabs.d.ts +7 -5
- package/dist/src/shell/dockTabs.js +4 -4
- package/dist/src/shell/reportedPanel.d.ts +12 -0
- package/dist/src/shell/reportedPanel.js +19 -0
- package/dist/src/shell/shell.css +56 -1
- package/dist/src/shell/strings.d.ts +2 -0
- package/dist/src/shell/strings.js +2 -0
- package/dist/src/shell/usePanelDocs.d.ts +11 -0
- package/dist/src/shell/usePanelDocs.js +29 -0
- package/dist/src/shell/useRequiredFirst.d.ts +27 -0
- package/dist/src/shell/useRequiredFirst.js +67 -0
- package/dist/src/storyCheck.d.ts +2 -0
- package/dist/src/storyCheck.js +2 -1
- package/dist/src/storyGlobals.js +2 -1
- package/dist/src/storyTree.d.ts +11 -1
- package/dist/src/storyTree.js +3 -1
- package/dist/src/viewState.d.ts +7 -0
- package/dist/src/viewState.js +13 -0
- package/dist/src/vite/agent.types.d.ts +12 -0
- package/dist/src/vite/agentApi.js +6 -1
- package/dist/src/vite/agentRoutes.js +18 -4
- package/dist/src/vite/builtTexts.d.ts +2 -2
- package/dist/src/vite/builtTexts.js +7 -6
- package/dist/src/vite/configFile.d.ts +3 -2
- package/dist/src/vite/configFile.js +63 -14
- package/dist/src/vite/docPagesModule.d.ts +10 -7
- package/dist/src/vite/docPagesModule.js +14 -11
- package/dist/src/vite/docsExtractor.d.ts +6 -0
- package/dist/src/vite/docsExtractor.js +13 -0
- package/dist/src/vite/fileAccess.d.ts +51 -0
- package/dist/src/vite/fileAccess.js +109 -0
- package/dist/src/vite/forgebookPlugin.d.ts +1 -1
- package/dist/src/vite/forgebookPlugin.js +61 -96
- package/dist/src/vite/framesServer.js +21 -5
- package/dist/src/vite/generatedModules.d.ts +23 -0
- package/dist/src/vite/generatedModules.js +58 -0
- package/dist/src/vite/hotSnippets.js +7 -3
- package/dist/src/vite/indexStoryFile.d.ts +2 -2
- package/dist/src/vite/indexStoryFile.js +2 -2
- package/dist/src/vite/indexedParameters.d.ts +1 -1
- package/dist/src/vite/indexedParameters.js +21 -1
- package/dist/src/vite/originGuard.d.ts +14 -4
- package/dist/src/vite/originGuard.js +15 -6
- package/dist/src/vite/pages.d.ts +3 -2
- package/dist/src/vite/pages.js +23 -16
- package/dist/src/vite/pluginIds.d.ts +9 -2
- package/dist/src/vite/pluginIds.js +21 -6
- package/dist/src/vite/pluginModules.d.ts +39 -10
- package/dist/src/vite/pluginModules.js +67 -22
- package/dist/src/vite/prebundled.js +2 -1
- package/dist/src/vite/searchModule.d.ts +6 -4
- package/dist/src/vite/searchModule.js +8 -7
- package/dist/src/vite/storyFilesModule.d.ts +59 -27
- package/dist/src/vite/storyFilesModule.js +88 -58
- package/dist/src/vite/storyPatterns.d.ts +39 -0
- package/dist/src/vite/storyPatterns.js +100 -0
- package/dist/src/vite/textQueries.d.ts +7 -1
- package/dist/src/vite/textQueries.js +7 -1
- package/package.json +2 -2
package/dist/src/propGroups.js
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A component's props in their groups, as its TSDoc names them (`@group Selection`): the API reference and the Props
|
|
3
|
-
* tab list them the same way.
|
|
4
|
-
*
|
|
5
|
-
* @remarks
|
|
6
|
-
* The props that name no group come first: they are the ones an author left out of every group, most often the few
|
|
7
|
-
* every use of the component sets (`value`, `onChange`). Then each group, in the order a configuration names them
|
|
8
|
-
* (the component's meta, then the catalogue), the others in the order they first appear among the props. A component
|
|
9
|
-
* whose props name no group is one list, as it always was. Every group starts open: a reader folds one away, never the
|
|
10
|
-
* catalogue. Pure.
|
|
11
|
-
*
|
|
12
|
-
* @module
|
|
13
|
-
*/
|
|
14
1
|
/**
|
|
15
2
|
* The order of the groups named first: the component's own (its meta's `parameters.propGroups`), then the
|
|
16
3
|
* catalogue's (`propGroups` in its configuration) not already named, each once.
|
|
@@ -57,3 +44,70 @@ export const groupedProps = (props, order) => {
|
|
|
57
44
|
* @param props - the props.
|
|
58
45
|
*/
|
|
59
46
|
export const hasPropGroups = (props) => props.some((prop) => prop.group !== undefined);
|
|
47
|
+
/** The layouts the required props can take, as a configuration and `parameters.requiredFirst` write them. */
|
|
48
|
+
export const REQUIRED_FIRST_LAYOUTS = ["in-groups", "section", false];
|
|
49
|
+
/**
|
|
50
|
+
* The layout a reader sees: the narrowest the developer set (the story's or the component's, else the catalogue's),
|
|
51
|
+
* with the reader's switch over it. Turned off, the source's order; turned on where the developer chose the source's
|
|
52
|
+
* order, the catalogue's layout, else `in-groups`.
|
|
53
|
+
*
|
|
54
|
+
* @param own - the story's or the component's setting; none leaves it to the catalogue.
|
|
55
|
+
* @param catalogue - the catalogue's setting.
|
|
56
|
+
* @param isReaderOn - the reader's switch; none when the reader never turned it.
|
|
57
|
+
*/
|
|
58
|
+
export const requiredFirstLayout = (own, catalogue, isReaderOn) => {
|
|
59
|
+
const chosen = own ?? catalogue;
|
|
60
|
+
if (isReaderOn === undefined)
|
|
61
|
+
return chosen;
|
|
62
|
+
if (!isReaderOn)
|
|
63
|
+
return false;
|
|
64
|
+
if (chosen !== false)
|
|
65
|
+
return chosen;
|
|
66
|
+
return catalogue === false ? "in-groups" : catalogue;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Whether putting the required props first changes anything: a required prop and one that is not. A table without
|
|
70
|
+
* shows no "Required first" switch.
|
|
71
|
+
*
|
|
72
|
+
* @param props - the props shown.
|
|
73
|
+
*/
|
|
74
|
+
export const canOrderRequired = (props) => props.some((prop) => prop.isRequired) && props.some((prop) => !prop.isRequired);
|
|
75
|
+
/**
|
|
76
|
+
* Props with the required ones first, the rest in their order.
|
|
77
|
+
*
|
|
78
|
+
* @param props - the props, in their order.
|
|
79
|
+
*/
|
|
80
|
+
export const requiredPropsFirst = (props) => [
|
|
81
|
+
...props.filter((prop) => prop.isRequired),
|
|
82
|
+
...props.filter((prop) => !prop.isRequired)
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* Props laid out for reading: in their groups (`groupedProps`), the events apart, the required ones where the layout
|
|
86
|
+
* puts them.
|
|
87
|
+
*
|
|
88
|
+
* @param props - the props, events among them (`kind: "event"`), in the order of the source.
|
|
89
|
+
* @param order - the groups to show first, in this order (`propGroupOrder`).
|
|
90
|
+
* @param layout - where the required props go (`requiredFirstLayout`).
|
|
91
|
+
*/
|
|
92
|
+
export const laidOutProps = (props, order, layout) => {
|
|
93
|
+
const isEvent = (prop) => prop.kind === "event";
|
|
94
|
+
const listed = props.filter((prop) => !isEvent(prop));
|
|
95
|
+
const groups = groupedProps(listed, order);
|
|
96
|
+
const events = props.filter(isEvent);
|
|
97
|
+
if (layout === false)
|
|
98
|
+
return { required: [], groups, events };
|
|
99
|
+
if (layout === "in-groups" || !hasPropGroups(listed)) {
|
|
100
|
+
return {
|
|
101
|
+
required: [],
|
|
102
|
+
groups: groups.map((group) => ({ name: group.name, props: requiredPropsFirst(group.props) })),
|
|
103
|
+
events: requiredPropsFirst(events)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
required: [...groups.flatMap((group) => group.props), ...events].filter((prop) => prop.isRequired),
|
|
108
|
+
groups: groups
|
|
109
|
+
.map((group) => ({ name: group.name, props: group.props.filter((prop) => !prop.isRequired) }))
|
|
110
|
+
.filter((group) => group.props.length > 0),
|
|
111
|
+
events: events.filter((prop) => !prop.isRequired)
|
|
112
|
+
};
|
|
113
|
+
};
|
|
@@ -5,22 +5,27 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @remarks
|
|
7
7
|
* Props whose TSDoc names a group (`@group Selection`) are listed under its heading, after the props that name none
|
|
8
|
-
* (`propGroups.ts`), each open, so a reader may fold one away.
|
|
8
|
+
* (`propGroups.ts`), each open, so a reader may fold one away. The required props come first in each group and among
|
|
9
|
+
* the events, or in one "Required" section before the groups, each naming its group, as the component, the catalogue
|
|
10
|
+
* and the reader's switch in the header say (`useRequiredFirst.ts`). A prop whose type is an object the
|
|
9
11
|
* package documents (`texts?: FileFinderTexts`) unfolds in place to list that type's members; they are drawn only
|
|
10
12
|
* once unfolded, so a library with large text types keeps a light page.
|
|
11
13
|
*
|
|
12
14
|
* @module
|
|
13
15
|
*/
|
|
16
|
+
import type { RequiredFirst } from "@forgebook/story-format/csf";
|
|
14
17
|
import { type ReactNode } from "react";
|
|
15
18
|
import type { ComponentDocs } from "../componentDocs.ts";
|
|
16
19
|
/**
|
|
17
20
|
* The API reference.
|
|
18
21
|
*
|
|
19
|
-
* @param props - the component's docs, whether it is a hook's or a function's (its stories are about logic),
|
|
20
|
-
* prop groups its meta lists first, in order (`parameters.propGroups`), ahead of the catalogue's
|
|
22
|
+
* @param props - the component's docs, whether it is a hook's or a function's (its stories are about logic), the
|
|
23
|
+
* prop groups its meta lists first, in order (`parameters.propGroups`), ahead of the catalogue's, none by default;
|
|
24
|
+
* and where its meta puts the required props (`parameters.requiredFirst`), none for the catalogue's.
|
|
21
25
|
*/
|
|
22
|
-
export declare const ApiReference: ({ docs, isLogic, componentGroups }: {
|
|
26
|
+
export declare const ApiReference: ({ docs, isLogic, componentGroups, requiredFirst }: {
|
|
23
27
|
readonly docs: ComponentDocs;
|
|
24
28
|
readonly isLogic: boolean;
|
|
25
29
|
readonly componentGroups?: readonly string[];
|
|
30
|
+
readonly requiredFirst?: RequiredFirst | undefined;
|
|
26
31
|
}) => ReactNode;
|
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* The API reference on a component's page: its props and its events, drawn from its types and TSDoc, as a table
|
|
4
|
-
* (prop, type, default, what it does), never written by hand, then the same for each part the meta names in
|
|
5
|
-
* `subcomponents`. The table scrolls sideways on its own when the page is narrower than it.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* Props whose TSDoc names a group (`@group Selection`) are listed under its heading, after the props that name none
|
|
9
|
-
* (`propGroups.ts`), each open, so a reader may fold one away. A prop whose type is an object the
|
|
10
|
-
* package documents (`texts?: FileFinderTexts`) unfolds in place to list that type's members; they are drawn only
|
|
11
|
-
* once unfolded, so a library with large text types keeps a light page.
|
|
12
|
-
*
|
|
13
|
-
* @module
|
|
14
|
-
*/
|
|
15
2
|
import { useState } from "react";
|
|
16
|
-
import {
|
|
3
|
+
import { canOrderRequired, laidOutProps, propGroupOrder } from "../propGroups.js";
|
|
17
4
|
import { Code } from "./Code.js";
|
|
18
5
|
import { CodeText } from "./CodeText.js";
|
|
19
6
|
import { useCatalogue } from "./catalogueContext.js";
|
|
20
7
|
import { Icon } from "./icons.js";
|
|
21
8
|
import { LATER_STRINGS } from "./laterStrings.js";
|
|
9
|
+
import { RequiredFirstSwitch } from "./RequiredFirstSwitch.js";
|
|
22
10
|
import { SignatureReference } from "./SignatureReference.js";
|
|
23
11
|
import { STRINGS } from "./strings.js";
|
|
12
|
+
import { useRequiredFirst } from "./useRequiredFirst.js";
|
|
24
13
|
/** A prop's or an event's type as one line of TypeScript: an event's signature as an arrow. */
|
|
25
14
|
const typeText = (row) => row.signature === undefined
|
|
26
15
|
? row.type
|
|
@@ -31,66 +20,85 @@ const NAME_HEADINGS = {
|
|
|
31
20
|
events: () => LATER_STRINGS.apiEvent,
|
|
32
21
|
members: () => LATER_STRINGS.apiMember
|
|
33
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Where a row of the "Required" section comes from, beside its name: "Event" for an event, which the section gathers
|
|
25
|
+
* with the props, else its group, when it names one.
|
|
26
|
+
*
|
|
27
|
+
* @param props - the row.
|
|
28
|
+
*/
|
|
29
|
+
const RowPlace = ({ row }) => {
|
|
30
|
+
const place = row.signature === undefined ? row.group : LATER_STRINGS.apiEvent;
|
|
31
|
+
return place === undefined ? null : _jsx("span", { className: "forgebook-prop__group", children: place });
|
|
32
|
+
};
|
|
34
33
|
/**
|
|
35
34
|
* One row, and under it, once unfolded, the members of its object type across the whole table.
|
|
36
35
|
*
|
|
37
|
-
* @param props - the row, whether the table has a Default column, its number of columns,
|
|
38
|
-
*
|
|
36
|
+
* @param props - the row, whether the table has a Default column, its number of columns, the part it belongs to (the
|
|
37
|
+
* row says its name, `data-prop`, for an address to open it), and whether it names its group (in "Required").
|
|
39
38
|
*/
|
|
40
|
-
const Row = ({ row, hasDefaults, columns, part }) => {
|
|
39
|
+
const Row = ({ row, hasDefaults, columns, part, showsGroup }) => {
|
|
41
40
|
const [isOpen, setOpen] = useState(false);
|
|
42
41
|
const { members } = row;
|
|
43
|
-
return (_jsxs(_Fragment, { children: [_jsxs("tr", { "data-prop": part === undefined ? row.name : `${part}.${row.name}`, children: [_jsxs("th", { scope: "row", children: [_jsx("code", { className: "forgebook-api__name", children: row.name }), row.isRequired ? (_jsx("span", { className: "forgebook-prop__required", title: STRINGS.required, children: "*" })) : null] }), _jsxs("td", { className: "forgebook-api__type", children: [_jsx(Code, { text: typeText(row), names: row.signature?.parameters.map((parameter) => parameter.name) ?? [] }), members === undefined ? null : (_jsxs("button", { type: "button", className: "forgebook-api__members-toggle", "aria-expanded": isOpen, "aria-label": `${LATER_STRINGS.membersOf(row.type)}: ${LATER_STRINGS.memberCount(members.length)}`, onClick: () => setOpen(!isOpen), children: [_jsx(Icon, { name: "chevronRight", size: 12 }), LATER_STRINGS.memberCount(members.length)] }))] }), hasDefaults ? (_jsx("td", { children: row.defaultValue === undefined ? null : (_jsx("code", { className: "forgebook-inline-code", children: row.defaultValue })) })) : null, _jsx("td", { children: _jsx(CodeText, { text: row.description }) })] }), members !== undefined && isOpen ? (_jsx("tr", { className: "forgebook-api__members", children: _jsx("td", { colSpan: columns, children: _jsx(PropTable, { rows: members, kind: "members", label: LATER_STRINGS.membersOf(row.type), part: part === undefined ? row.name : `${part}.${row.name}` }) }) })) : null] }));
|
|
42
|
+
return (_jsxs(_Fragment, { children: [_jsxs("tr", { "data-prop": part === undefined ? row.name : `${part}.${row.name}`, children: [_jsxs("th", { scope: "row", children: [_jsx("code", { className: "forgebook-api__name", children: row.name }), row.isRequired ? (_jsx("span", { className: "forgebook-prop__required", title: STRINGS.required, children: "*" })) : null, showsGroup ? _jsx(RowPlace, { row: row }) : null] }), _jsxs("td", { className: "forgebook-api__type", children: [_jsx(Code, { text: typeText(row), names: row.signature?.parameters.map((parameter) => parameter.name) ?? [] }), members === undefined ? null : (_jsxs("button", { type: "button", className: "forgebook-api__members-toggle", "aria-expanded": isOpen, "aria-label": `${LATER_STRINGS.membersOf(row.type)}: ${LATER_STRINGS.memberCount(members.length)}`, onClick: () => setOpen(!isOpen), children: [_jsx(Icon, { name: "chevronRight", size: 12 }), LATER_STRINGS.memberCount(members.length)] }))] }), hasDefaults ? (_jsx("td", { children: row.defaultValue === undefined ? null : (_jsx("code", { className: "forgebook-inline-code", children: row.defaultValue })) })) : null, _jsx("td", { children: _jsx(CodeText, { text: row.description }) })] }), members !== undefined && isOpen ? (_jsx("tr", { className: "forgebook-api__members", children: _jsx("td", { colSpan: columns, children: _jsx(PropTable, { rows: members, kind: "members", label: LATER_STRINGS.membersOf(row.type), part: part === undefined ? row.name : `${part}.${row.name}` }) }) })) : null] }));
|
|
44
43
|
};
|
|
45
44
|
/**
|
|
46
45
|
* A table of props, events or members: the Default column only when one of them has a default.
|
|
47
46
|
*
|
|
48
|
-
* @param props - the rows, what they are, the table's name when it needs one (a members table),
|
|
49
|
-
* belong to.
|
|
47
|
+
* @param props - the rows, what they are, the table's name when it needs one (a members table), the part they
|
|
48
|
+
* belong to, and whether each names its group (the "Required" section).
|
|
50
49
|
*/
|
|
51
|
-
const PropTable = ({ rows, kind, label, part }) => {
|
|
50
|
+
const PropTable = ({ rows, kind, label, part, showsGroups = false }) => {
|
|
52
51
|
const hasDefaults = kind !== "events" && rows.some((row) => row.defaultValue !== undefined);
|
|
53
|
-
return (_jsx("div", { className: "forgebook-api", children: _jsxs("table", { className: "forgebook-api__table", "aria-label": label, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: NAME_HEADINGS[kind]() }), _jsx("th", { scope: "col", children: kind === "events" ? LATER_STRINGS.apiSignature : STRINGS.apiType }), hasDefaults ? _jsx("th", { scope: "col", children: LATER_STRINGS.apiDefault }) : null, _jsx("th", { scope: "col", children: STRINGS.apiWhat })] }) }), _jsx("tbody", { children: rows.map((row) => (_jsx(Row, { row: row, hasDefaults: hasDefaults, columns: hasDefaults ? 4 : 3, part: part }, row.name))) })] }) }));
|
|
52
|
+
return (_jsx("div", { className: "forgebook-api", children: _jsxs("table", { className: "forgebook-api__table", "aria-label": label, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: NAME_HEADINGS[kind]() }), _jsx("th", { scope: "col", children: kind === "events" ? LATER_STRINGS.apiSignature : STRINGS.apiType }), hasDefaults ? _jsx("th", { scope: "col", children: LATER_STRINGS.apiDefault }) : null, _jsx("th", { scope: "col", children: STRINGS.apiWhat })] }) }), _jsx("tbody", { children: rows.map((row) => (_jsx(Row, { row: row, hasDefaults: hasDefaults, columns: hasDefaults ? 4 : 3, part: part, showsGroup: showsGroups }, row.name))) })] }) }));
|
|
54
53
|
};
|
|
55
54
|
/**
|
|
56
55
|
* A group of props: the props that name none as a table alone, a named group under its heading, open until a reader
|
|
57
|
-
* folds it.
|
|
56
|
+
* folds it. The "Required" section is drawn as a group, each of its props naming its own.
|
|
58
57
|
*
|
|
59
|
-
* @param props - the group, the level of its heading,
|
|
58
|
+
* @param props - the group, the level of its heading, the part it belongs to, and whether it is the "Required"
|
|
59
|
+
* section.
|
|
60
60
|
*/
|
|
61
|
-
const GroupTable = ({ group, level, part }) => {
|
|
61
|
+
const GroupTable = ({ group, level, part, isRequired = false }) => {
|
|
62
62
|
if (group.name === undefined)
|
|
63
63
|
return _jsx(PropTable, { rows: group.props, kind: "props", part: part });
|
|
64
64
|
const Heading = level === 3 ? "h3" : "h4";
|
|
65
|
-
return (_jsxs("details", { className: "forgebook-fold forgebook-api__group", open: true, children: [_jsxs("summary", { className: "forgebook-fold__summary", children: [_jsx(Icon, { name: "chevronRight", size: 14 }), _jsx(Heading, { className: "forgebook-fold__name", children: group.name }), _jsx("span", { className: "forgebook-fold__count", children: STRINGS.propCount(group.props.length) })] }), _jsx(PropTable, { rows: group.props, kind: "props", part: part })] }));
|
|
65
|
+
return (_jsxs("details", { className: "forgebook-fold forgebook-api__group", "data-required": isRequired || undefined, open: true, children: [_jsxs("summary", { className: "forgebook-fold__summary", children: [_jsx(Icon, { name: "chevronRight", size: 14 }), _jsx(Heading, { className: "forgebook-fold__name", children: group.name }), _jsx("span", { className: "forgebook-fold__count", children: STRINGS.propCount(group.props.length) })] }), _jsx(PropTable, { rows: group.props, kind: "props", part: part, showsGroups: isRequired })] }));
|
|
66
66
|
};
|
|
67
67
|
/**
|
|
68
|
-
* A component's or a part's props in their groups, then its events under their own heading
|
|
68
|
+
* A component's or a part's props in their groups, then its events under their own heading, the required ones where
|
|
69
|
+
* the layout puts them.
|
|
69
70
|
*
|
|
70
|
-
* @param props - the props, the groups to list first, the
|
|
71
|
-
* part's) and the part they belong to.
|
|
71
|
+
* @param props - the props, the groups to list first, the layout of the required props, the level of the headings
|
|
72
|
+
* (under the section's, or under a part's) and the part they belong to.
|
|
72
73
|
*/
|
|
73
|
-
const PropsAndEvents = ({ props, groupOrder, level, part }) => {
|
|
74
|
+
const PropsAndEvents = ({ props, groupOrder, layout, level, part }) => {
|
|
74
75
|
const EventsHeading = level === 3 ? "h3" : "h4";
|
|
75
|
-
const groups =
|
|
76
|
-
|
|
77
|
-
return (_jsxs(_Fragment, { children: [groups.map((group) => (_jsx(GroupTable, { group: group, level: level, part: part }, group.name ?? ""))), events.length === 0 ? null : (_jsxs(_Fragment, { children: [_jsx(EventsHeading, { children: LATER_STRINGS.events }), _jsx(PropTable, { rows: events, kind: "events", part: part })] }))] }));
|
|
76
|
+
const { required, groups, events } = laidOutProps(props, groupOrder, layout);
|
|
77
|
+
return (_jsxs(_Fragment, { children: [required.length === 0 ? null : (_jsx(GroupTable, { group: { name: STRINGS.required, props: required }, level: level, part: part, isRequired: true })), groups.map((group) => (_jsx(GroupTable, { group: group, level: level, part: part }, group.name ?? ""))), events.length === 0 ? null : (_jsxs(_Fragment, { children: [_jsx(EventsHeading, { children: LATER_STRINGS.events }), _jsx(PropTable, { rows: events, kind: "events", part: part })] }))] }));
|
|
78
78
|
};
|
|
79
79
|
/**
|
|
80
80
|
* A part's reference: its name, its summary, its props and events.
|
|
81
81
|
*
|
|
82
|
-
* @param props - the part's docs,
|
|
82
|
+
* @param props - the part's docs, the groups to list first, and the layout of the required props.
|
|
83
83
|
*/
|
|
84
|
-
const SubcomponentReference = ({ docs, groupOrder }) => (_jsxs("section", { className: "forgebook-api__subcomponent", "aria-label": docs.name, "data-prop": docs.name, children: [_jsx("h3", { children: _jsx("code", { className: "forgebook-inline-code", children: docs.name }) }), docs.description === "" ? null : (_jsx("p", { children: _jsx(CodeText, { text: docs.description }) })), _jsx(PropsAndEvents, { props: docs.props, groupOrder: groupOrder, level: 4, part: docs.name })] }));
|
|
84
|
+
const SubcomponentReference = ({ docs, groupOrder, layout }) => (_jsxs("section", { className: "forgebook-api__subcomponent", "aria-label": docs.name, "data-prop": docs.name, children: [_jsx("h3", { children: _jsx("code", { className: "forgebook-inline-code", children: docs.name }) }), docs.description === "" ? null : (_jsx("p", { children: _jsx(CodeText, { text: docs.description }) })), _jsx(PropsAndEvents, { props: docs.props, groupOrder: groupOrder, layout: layout, level: 4, part: docs.name })] }));
|
|
85
85
|
/** No group named first. */
|
|
86
86
|
const NO_GROUPS = [];
|
|
87
87
|
/**
|
|
88
88
|
* The API reference.
|
|
89
89
|
*
|
|
90
|
-
* @param props - the component's docs, whether it is a hook's or a function's (its stories are about logic),
|
|
91
|
-
* prop groups its meta lists first, in order (`parameters.propGroups`), ahead of the catalogue's
|
|
90
|
+
* @param props - the component's docs, whether it is a hook's or a function's (its stories are about logic), the
|
|
91
|
+
* prop groups its meta lists first, in order (`parameters.propGroups`), ahead of the catalogue's, none by default;
|
|
92
|
+
* and where its meta puts the required props (`parameters.requiredFirst`), none for the catalogue's.
|
|
92
93
|
*/
|
|
93
|
-
export const ApiReference = ({ docs, isLogic, componentGroups = NO_GROUPS }) => {
|
|
94
|
+
export const ApiReference = ({ docs, isLogic, componentGroups = NO_GROUPS, requiredFirst }) => {
|
|
94
95
|
const groupOrder = propGroupOrder(componentGroups, useCatalogue().propGroups);
|
|
95
|
-
|
|
96
|
+
const ordering = useRequiredFirst(requiredFirst);
|
|
97
|
+
// a hook's or a function's page documents its call, not the props of a component
|
|
98
|
+
const isSignature = isLogic && docs.signature !== undefined;
|
|
99
|
+
const shown = [
|
|
100
|
+
...(isSignature ? [] : docs.props),
|
|
101
|
+
...docs.subcomponents.flatMap((subcomponent) => subcomponent.props)
|
|
102
|
+
];
|
|
103
|
+
return (_jsxs("section", { className: "forgebook-doc__section", "aria-labelledby": "forgebook-api", children: [_jsxs("div", { className: "forgebook-api__head", children: [_jsx("h2", { id: "forgebook-api", children: LATER_STRINGS.apiReference }), canOrderRequired(shown) ? _jsx(RequiredFirstSwitch, { state: ordering }) : null] }), isSignature ? (_jsx(SignatureReference, { name: docs.name, signature: docs.signature })) : (_jsx(PropsAndEvents, { props: docs.props, groupOrder: groupOrder, layout: ordering.layout, level: 3 })), docs.subcomponents.map((subcomponent) => (_jsx(SubcomponentReference, { docs: subcomponent, groupOrder: groupOrder, layout: ordering.layout }, subcomponent.name)))] }));
|
|
96
104
|
};
|
|
@@ -133,7 +133,7 @@ const Section = ({ id, ...props }) => {
|
|
|
133
133
|
case "prose":
|
|
134
134
|
return _jsx(AudienceProse, { page: page, reading: AUDIENCE_PAGES[props.audience].prose });
|
|
135
135
|
case "api":
|
|
136
|
-
return _jsx(PageApi, { docs: docs, isLogic: isLogic, componentGroups: component.propGroups });
|
|
136
|
+
return (_jsx(PageApi, { docs: docs, isLogic: isLogic, componentGroups: component.propGroups, requiredFirst: component.requiredFirst }));
|
|
137
137
|
case "parts":
|
|
138
138
|
return _jsx(PartsSection, { docs: docs });
|
|
139
139
|
case "pages":
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module
|
|
15
15
|
*/
|
|
16
|
+
import type { RequiredFirst } from "@forgebook/story-format/csf";
|
|
16
17
|
import { type ReactNode } from "react";
|
|
17
18
|
import { type ComponentDocs } from "../componentDocs.ts";
|
|
18
19
|
import { type StoryTree, type TreeComponent, type TreeStory } from "../storyTree.ts";
|
|
@@ -82,12 +83,14 @@ export declare const Lead: ({ page, docs }: {
|
|
|
82
83
|
/**
|
|
83
84
|
* The API reference; nothing without props, or a signature for what is about logic.
|
|
84
85
|
*
|
|
85
|
-
* @param props - the component's docs, whether it is about logic,
|
|
86
|
+
* @param props - the component's docs, whether it is about logic, the prop groups its meta lists first, and where its
|
|
87
|
+
* meta puts the required props.
|
|
86
88
|
*/
|
|
87
|
-
export declare const PageApi: ({ docs, isLogic, componentGroups }: {
|
|
89
|
+
export declare const PageApi: ({ docs, isLogic, componentGroups, requiredFirst }: {
|
|
88
90
|
readonly docs: ComponentDocs | undefined;
|
|
89
91
|
readonly isLogic: boolean;
|
|
90
92
|
readonly componentGroups: readonly string[];
|
|
93
|
+
readonly requiredFirst: RequiredFirst | undefined;
|
|
91
94
|
}) => ReactNode;
|
|
92
95
|
/**
|
|
93
96
|
* A component's page.
|
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* A component's page, as a library documents a component: what it is, its demo (the story marked `main`) with
|
|
4
|
-
* its code, the text of its documentation file, its API reference drawn from its types, then every other story as an
|
|
5
|
-
* example.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* The frames load a few at a time (`useFrameQueue`), in the page's order, the demo first, each as it comes near the
|
|
9
|
-
* visible part of the page: a component may have twenty stories. The view's live part reaches every frame as a message, as
|
|
10
|
-
* on the story page; the props panel and Replay belong to a story's own page, which each story's name links to. The
|
|
11
|
-
* documentation file's HTML is the package's own, made at build time (`vite/componentPage.ts`). Who reads the page
|
|
12
|
-
* reorders and filters what goes around the frames (`audienceLayout.ts`); what only one audience wants loads on first use,
|
|
13
|
-
* its data alongside the sections' code rather than after it.
|
|
14
|
-
*
|
|
15
|
-
* @module
|
|
16
|
-
*/
|
|
17
2
|
import { lazy, Suspense, useEffect, useMemo, useRef } from "react";
|
|
18
3
|
import { groupLabel } from "../catalogue.js";
|
|
19
4
|
import { codeNameOf } from "../componentDocs.js";
|
|
@@ -95,9 +80,10 @@ const AudienceSlot = ({ place, audience, children }) => audience === undefined ?
|
|
|
95
80
|
/**
|
|
96
81
|
* The API reference; nothing without props, or a signature for what is about logic.
|
|
97
82
|
*
|
|
98
|
-
* @param props - the component's docs, whether it is about logic,
|
|
83
|
+
* @param props - the component's docs, whether it is about logic, the prop groups its meta lists first, and where its
|
|
84
|
+
* meta puts the required props.
|
|
99
85
|
*/
|
|
100
|
-
export const PageApi = ({ docs, isLogic, componentGroups }) => docs === undefined || (docs.props.length === 0 && !(isLogic && docs.signature !== undefined)) ? null : (_jsx(ApiReference, { docs: docs, isLogic: isLogic, componentGroups: componentGroups }));
|
|
86
|
+
export const PageApi = ({ docs, isLogic, componentGroups, requiredFirst }) => docs === undefined || (docs.props.length === 0 && !(isLogic && docs.signature !== undefined)) ? null : (_jsx(ApiReference, { docs: docs, isLogic: isLogic, componentGroups: componentGroups, requiredFirst: requiredFirst }));
|
|
101
87
|
/**
|
|
102
88
|
* A component's page.
|
|
103
89
|
*
|
|
@@ -173,5 +159,5 @@ export const ComponentPage = ({ component, anchor, view, touchOverrides, onTouch
|
|
|
173
159
|
usedIn,
|
|
174
160
|
pageRef
|
|
175
161
|
};
|
|
176
|
-
return (_jsxs("div", { className: "forgebook-workspace", children: [header, anchor === undefined ? null : (_jsx(Suspense, { fallback: null, children: _jsx(AnchorScroll, { pageRef: pageRef, anchor: anchor, visit: scroll.visit, isReady: docs.status !== "loading" && page.status !== "loading" }) })), _jsx("div", { ref: pageRef, className: "forgebook-scroll", children: _jsxs("div", { className: "forgebook-doc", "data-audience": view.audience === "everyone" ? undefined : view.audience, children: [_jsx(AudienceSlot, { place: "beforeDemo", audience: audienceProps, children: _jsx(Lead, { page: page, docs: componentDocs }) }), demo === undefined ? null : (_jsx(Example, { ...context, story: demo, isDemo: true, isMounted: queue.mounted.has(demo.id), observe: queue.observerOf(demo.id) })), _jsxs(AudienceSlot, { place: "afterDemo", audience: audienceProps, children: [_jsx(Prose, { page: page }), _jsx(PageApi, { docs: componentDocs, isLogic: isLogic, componentGroups: component.propGroups })] }), examples.length === 0 ? null : (_jsxs("section", { className: "forgebook-doc__section", "aria-labelledby": "forgebook-examples", children: [_jsx("h2", { id: "forgebook-examples", children: LATER_STRINGS.examples }), groupedStories(examples, catalogue.storyGroups.map((group) => group.id)).map((section) => (_jsxs("section", { className: "forgebook-doc__group", children: [section.group === undefined ? null : (_jsx("h3", { className: "forgebook-doc__group-name", children: groupLabel(catalogue.storyGroups, section.group) })), section.stories.map((story) => (_jsx(Example, { ...context, story: story, isDemo: false, isMounted: queue.mounted.has(story.id), observe: queue.observerOf(story.id) }, story.id)))] }, section.group ?? "")))] })), _jsx(AudienceSlot, { place: "afterExamples", audience: audienceProps, children: usedIn === undefined ? null : _jsx(UsedIn, { title: component.title, ...usedIn }) })] }, `${component.file}|${component.title}`) })] }));
|
|
162
|
+
return (_jsxs("div", { className: "forgebook-workspace", children: [header, anchor === undefined ? null : (_jsx(Suspense, { fallback: null, children: _jsx(AnchorScroll, { pageRef: pageRef, anchor: anchor, visit: scroll.visit, isReady: docs.status !== "loading" && page.status !== "loading" }) })), _jsx("div", { ref: pageRef, className: "forgebook-scroll", children: _jsxs("div", { className: "forgebook-doc", "data-audience": view.audience === "everyone" ? undefined : view.audience, children: [_jsx(AudienceSlot, { place: "beforeDemo", audience: audienceProps, children: _jsx(Lead, { page: page, docs: componentDocs }) }), demo === undefined ? null : (_jsx(Example, { ...context, story: demo, isDemo: true, isMounted: queue.mounted.has(demo.id), observe: queue.observerOf(demo.id) })), _jsxs(AudienceSlot, { place: "afterDemo", audience: audienceProps, children: [_jsx(Prose, { page: page }), _jsx(PageApi, { docs: componentDocs, isLogic: isLogic, componentGroups: component.propGroups, requiredFirst: component.requiredFirst })] }), examples.length === 0 ? null : (_jsxs("section", { className: "forgebook-doc__section", "aria-labelledby": "forgebook-examples", children: [_jsx("h2", { id: "forgebook-examples", children: LATER_STRINGS.examples }), groupedStories(examples, catalogue.storyGroups.map((group) => group.id)).map((section) => (_jsxs("section", { className: "forgebook-doc__group", children: [section.group === undefined ? null : (_jsx("h3", { className: "forgebook-doc__group-name", children: groupLabel(catalogue.storyGroups, section.group) })), section.stories.map((story) => (_jsx(Example, { ...context, story: story, isDemo: false, isMounted: queue.mounted.has(story.id), observe: queue.observerOf(story.id) }, story.id)))] }, section.group ?? "")))] })), _jsx(AudienceSlot, { place: "afterExamples", audience: audienceProps, children: usedIn === undefined ? null : _jsx(UsedIn, { title: component.title, ...usedIn }) })] }, `${component.file}|${component.title}`) })] }));
|
|
177
163
|
};
|
|
@@ -132,7 +132,7 @@ const PropsBlock = ({ id, context }) => {
|
|
|
132
132
|
return _jsx(MissingBlock, { message: STRINGS.unknownComponent(id) });
|
|
133
133
|
if (docs.status === "error")
|
|
134
134
|
return _jsx(MissingBlock, { message: `${LATER_STRINGS.pageError}: ${docs.message}` });
|
|
135
|
-
return (_jsx("div", { ref: queue.observerOf(key), children: docs.status === "ready" && isComponentDocs(docs.value) ? (_jsx(ApiReference, { docs: docs.value, isLogic: isLogicComponent(location.component), componentGroups: location.component.propGroups })) : null }));
|
|
135
|
+
return (_jsx("div", { ref: queue.observerOf(key), children: docs.status === "ready" && isComponentDocs(docs.value) ? (_jsx(ApiReference, { docs: docs.value, isLogic: isLogicComponent(location.component), componentGroups: location.component.propGroups, requiredFirst: location.component.requiredFirst })) : null }));
|
|
136
136
|
};
|
|
137
137
|
/**
|
|
138
138
|
* A plugin's block; a fence no plugin of this catalogue draws is said, as a block that names what is not there.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One prop of the Props tab as it is drawn: its head (its name, whether it is required, its group in the "Required"
|
|
3
|
+
* group, its type), then its control, its toggle, or its description for reading.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { type ReactNode } from "react";
|
|
8
|
+
import { type PanelProp } from "../argTypes.ts";
|
|
9
|
+
import { type ArgValue } from "../messages.ts";
|
|
10
|
+
import { type OnValueChange } from "./PropFields.tsx";
|
|
11
|
+
/** A prop the story gives a text by its key (`storyText("key")`): the key, and whether the panel wrote over it. */
|
|
12
|
+
export type TextArgNote = {
|
|
13
|
+
/** The text's key. */
|
|
14
|
+
readonly key: string;
|
|
15
|
+
/** Whether the panel holds a value of its own for the prop. */
|
|
16
|
+
readonly isChanged: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** One adjustable prop with its control. */
|
|
19
|
+
export declare const PropControl: ({ prop, showsGroup, value, named, textArg, onValueChange }: {
|
|
20
|
+
readonly prop: PanelProp;
|
|
21
|
+
/** Whether it names its group: in the "Required" group. */
|
|
22
|
+
readonly showsGroup: boolean;
|
|
23
|
+
readonly value: ArgValue | undefined;
|
|
24
|
+
/** The values the source names for the prop's type, when it names some. */
|
|
25
|
+
readonly named: Readonly<Record<string, unknown>> | undefined;
|
|
26
|
+
/** Where the story's text for the prop comes from, when the story names one by its key. */
|
|
27
|
+
readonly textArg: TextArgNote | undefined;
|
|
28
|
+
readonly onValueChange: OnValueChange;
|
|
29
|
+
}) => ReactNode;
|
|
30
|
+
/** A toggle: its name in a compact checkbox, its description as the tooltip and for screen readers. */
|
|
31
|
+
export declare const Toggle: ({ prop, showsGroup, value, onValueChange }: {
|
|
32
|
+
readonly prop: PanelProp;
|
|
33
|
+
/** Whether it names its group: in the "Required" group. */
|
|
34
|
+
readonly showsGroup: boolean;
|
|
35
|
+
readonly value: ArgValue | undefined;
|
|
36
|
+
readonly onValueChange: OnValueChange;
|
|
37
|
+
}) => ReactNode;
|
|
38
|
+
/** A prop the panel cannot set: its name, its type and its description, for reading; its group when asked. */
|
|
39
|
+
export declare const ReadOnlyProp: ({ prop, showsGroup }: {
|
|
40
|
+
readonly prop: PanelProp;
|
|
41
|
+
readonly showsGroup: boolean;
|
|
42
|
+
}) => ReactNode;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* One prop of the Props tab as it is drawn: its head (its name, whether it is required, its group in the "Required"
|
|
4
|
+
* group, its type), then its control, its toggle, or its description for reading.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { useId } from "react";
|
|
9
|
+
import { isColorProp } from "../argTypes.js";
|
|
10
|
+
import { isNamedArg } from "../messages.js";
|
|
11
|
+
import { ChoiceControl, NumberField, textOf } from "./PropFields.js";
|
|
12
|
+
import { STRINGS } from "./strings.js";
|
|
13
|
+
/** A CSS colour as the colour picker takes it (`#rrggbb`), or black when the value is not one. */
|
|
14
|
+
const pickerColor = (value) => typeof value === "string" && /^#[0-9a-f]{6}$/i.test(value) ? value : "#000000";
|
|
15
|
+
/** A prop's group, beside its name in the "Required" group, which gathers props of every group. */
|
|
16
|
+
const GroupName = ({ prop, showsGroup }) => showsGroup && prop.group !== undefined ? _jsx("span", { className: "forgebook-prop__group", children: prop.group }) : null;
|
|
17
|
+
/**
|
|
18
|
+
* A prop's name, whether it is required, its group when asked, and its type (whole in the tooltip when it is cut).
|
|
19
|
+
*
|
|
20
|
+
* @param props - the prop, and whether it names its group.
|
|
21
|
+
*/
|
|
22
|
+
const PropHead = ({ prop, showsGroup }) => (_jsxs("div", { className: "forgebook-prop__head", children: [_jsxs("code", { className: "forgebook-prop__name", children: [prop.label ?? prop.name, prop.isRequired ? (_jsx("span", { className: "forgebook-prop__required", title: STRINGS.required, children: "*" })) : null] }), _jsx(GroupName, { prop: prop, showsGroup: showsGroup }), _jsx("code", { className: "forgebook-prop__type", title: prop.type, children: prop.type })] }));
|
|
23
|
+
/** A prop's description and default, under its control. */
|
|
24
|
+
const PropNote = ({ id, prop }) => prop.description === "" && prop.defaultValue === undefined ? null : (_jsxs("p", { id: id, className: "forgebook-prop__description", children: [prop.description, prop.defaultValue === undefined ? null : (_jsxs(_Fragment, { children: [prop.description === "" ? null : " ", STRINGS.defaultIs, " ", _jsx("code", { children: prop.defaultValue })] }))] }));
|
|
25
|
+
/**
|
|
26
|
+
* Where a prop's text comes from, under its field: the key of the story's text, and a way back to it once the panel
|
|
27
|
+
* wrote over it.
|
|
28
|
+
*
|
|
29
|
+
* @param props - the note, the prop's name and the value setter.
|
|
30
|
+
*/
|
|
31
|
+
const TextArgLine = ({ note, name, onValueChange }) => (_jsxs("p", { className: "forgebook-prop__text-arg", children: [STRINGS.fromTexts, " ", _jsx("code", { children: note.key }), note.isChanged ? (_jsx("button", { type: "button", className: "forgebook-text-button", onClick: () => onValueChange(name, undefined), children: STRINGS.backToText })) : null] }));
|
|
32
|
+
/** One adjustable prop with its control. */
|
|
33
|
+
export const PropControl = ({ prop, showsGroup, value, named, textArg, onValueChange }) => {
|
|
34
|
+
// the description is shown under the control and named by it, so the control's name is the prop alone
|
|
35
|
+
const descriptionId = useId();
|
|
36
|
+
const label = prop.label ?? prop.name;
|
|
37
|
+
if (named !== undefined) {
|
|
38
|
+
return (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup }), _jsxs("select", { "aria-label": label, "aria-describedby": descriptionId, value: isNamedArg(value) ? value.named : "", onChange: (event) => {
|
|
39
|
+
const chosen = event.target.value;
|
|
40
|
+
onValueChange(prop.name, chosen === "" ? undefined : { of: prop.type, named: chosen });
|
|
41
|
+
}, children: [_jsx("option", { value: "", children: STRINGS.unset }), Object.keys(named).map((name) => (_jsx("option", { value: name, children: name }, name)))] }), _jsx(PropNote, { id: descriptionId, prop: prop })] }));
|
|
42
|
+
}
|
|
43
|
+
if (prop.kind === "choice") {
|
|
44
|
+
return (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup }), _jsx(ChoiceControl, { prop: prop, value: value, descriptionId: descriptionId, onValueChange: onValueChange }), _jsx(PropNote, { id: descriptionId, prop: prop })] }));
|
|
45
|
+
}
|
|
46
|
+
if (prop.kind === "number") {
|
|
47
|
+
return (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup }), _jsx("div", { className: "forgebook-prop__field", children: _jsx(NumberField, { prop: prop, value: value, descriptionId: descriptionId, onValueChange: onValueChange }) }), _jsx(PropNote, { id: descriptionId, prop: prop })] }));
|
|
48
|
+
}
|
|
49
|
+
return (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup }), _jsxs("div", { className: "forgebook-prop__field", children: [_jsx("input", { type: "text", "aria-label": label, "aria-describedby": descriptionId, value: textOf(value), onChange: (event) => {
|
|
50
|
+
const text = event.target.value;
|
|
51
|
+
onValueChange(prop.name, text === "" ? undefined : text);
|
|
52
|
+
} }), isColorProp(prop) ? (_jsx("input", { type: "color", className: "forgebook-prop__color", "aria-label": STRINGS.pickColor(label), value: pickerColor(value), onChange: (event) => onValueChange(prop.name, event.target.value) })) : null] }), textArg === undefined ? null : (_jsx(TextArgLine, { note: textArg, name: prop.name, onValueChange: onValueChange })), _jsx(PropNote, { id: descriptionId, prop: prop })] }));
|
|
53
|
+
};
|
|
54
|
+
/** A toggle: its name in a compact checkbox, its description as the tooltip and for screen readers. */
|
|
55
|
+
export const Toggle = ({ prop, showsGroup, value, onValueChange }) => {
|
|
56
|
+
const descriptionId = useId();
|
|
57
|
+
return (_jsxs("label", { className: "forgebook-toggle", title: prop.description, children: [_jsx("input", { type: "checkbox", "aria-label": prop.label ?? prop.name, checked: value === true, "aria-describedby": descriptionId, onChange: (event) => onValueChange(prop.name, event.target.checked) }), _jsx("code", { children: prop.label ?? prop.name }), _jsx(GroupName, { prop: prop, showsGroup: showsGroup }), _jsx("span", { id: descriptionId, className: "forgebook-visually-hidden", children: prop.description })] }));
|
|
58
|
+
};
|
|
59
|
+
/** A prop the panel cannot set: its name, its type and its description, for reading; its group when asked. */
|
|
60
|
+
export const ReadOnlyProp = ({ prop, showsGroup }) => (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup }), _jsx("p", { className: "forgebook-prop__description", children: prop.description })] }));
|
|
@@ -10,21 +10,20 @@
|
|
|
10
10
|
* lists them (`propGroups.ts`): each group's controls, its toggles, then its props the panel cannot set. Each starts
|
|
11
11
|
* open, and a reader may fold it away. A component whose props name no group shows the tab as it always was.
|
|
12
12
|
*
|
|
13
|
+
* The required props come first among each kind of control and in each group, or in one "Required" group before the
|
|
14
|
+
* others, each naming its group, as the story, its component, the catalogue and the reader's switch at the top of the
|
|
15
|
+
* tab say (`useRequiredFirst.ts`).
|
|
16
|
+
*
|
|
13
17
|
* @module
|
|
14
18
|
*/
|
|
19
|
+
import type { RequiredFirst } from "@forgebook/story-format/csf";
|
|
15
20
|
import type { StoryControlsSetting } from "@forgebook/story-format/source";
|
|
16
|
-
import {
|
|
21
|
+
import type { ReactNode } from "react";
|
|
17
22
|
import { type PanelProp } from "../argTypes.ts";
|
|
18
|
-
import {
|
|
23
|
+
import type { ArgValue, PanelArgs } from "../messages.ts";
|
|
19
24
|
import type { NamedValues } from "../namedValues.ts";
|
|
25
|
+
import { type TextArgNote } from "./PropRows.tsx";
|
|
20
26
|
import type { WorstCaseState } from "./worstArgs.ts";
|
|
21
|
-
/** A prop the story gives a text by its key (`storyText("key")`): the key, and whether the panel wrote over it. */
|
|
22
|
-
export type TextArgNote = {
|
|
23
|
-
/** The text's key. */
|
|
24
|
-
readonly key: string;
|
|
25
|
-
/** Whether the panel holds a value of its own for the prop. */
|
|
26
|
-
readonly isChanged: boolean;
|
|
27
|
-
};
|
|
28
27
|
/** The tab's props. */
|
|
29
28
|
export type PropsTabProps = {
|
|
30
29
|
/** The props, from the component's types with the story's `argTypes` laid over them. */
|
|
@@ -48,10 +47,15 @@ export type PropsTabProps = {
|
|
|
48
47
|
* none by default.
|
|
49
48
|
*/
|
|
50
49
|
readonly componentGroups?: readonly string[];
|
|
50
|
+
/**
|
|
51
|
+
* Where the story puts the required props (`parameters.requiredFirst`, its own or its meta's); none for the
|
|
52
|
+
* catalogue's.
|
|
53
|
+
*/
|
|
54
|
+
readonly requiredFirst?: RequiredFirst | undefined;
|
|
51
55
|
};
|
|
52
56
|
/**
|
|
53
57
|
* The Props tab.
|
|
54
58
|
*
|
|
55
59
|
* @param props - the props, the values, which props the story lets the panel set, and the setters.
|
|
56
60
|
*/
|
|
57
|
-
export declare const PropsTab: ({ props, values, controls, onValueChange, onReset, worstCase, namedValues, textArgs, componentGroups }: PropsTabProps) => ReactNode;
|
|
61
|
+
export declare const PropsTab: ({ props, values, controls, onValueChange, onReset, worstCase, namedValues, textArgs, componentGroups, requiredFirst }: PropsTabProps) => ReactNode;
|