forgebook 0.4.0 → 0.4.1
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 +83 -3
- package/dist/bin/forgebook/agentText.js +7 -0
- package/dist/bin/forgebook/guide/commands.md +5 -0
- package/dist/bin/forgebook/guide/stories.md +7 -2
- package/dist/bin/forgebook/verifyBase.js +10 -2
- package/dist/bin/forgebook/verifyVerdict.js +27 -1
- package/dist/src/argControl.d.ts +51 -3
- package/dist/src/argTypes.js +4 -2
- package/dist/src/componentDocs.d.ts +12 -0
- package/dist/src/componentDocs.js +5 -1
- package/dist/src/controlCode.d.ts +27 -0
- package/dist/src/controlCode.js +41 -0
- package/dist/src/frame/controlValues.d.ts +39 -0
- package/dist/src/frame/controlValues.js +143 -0
- package/dist/src/frame/mountFrame.d.ts +3 -0
- package/dist/src/frame/mountFrame.js +8 -10
- package/dist/src/frame/panelControls.d.ts +67 -0
- package/dist/src/frame/panelControls.js +110 -0
- package/dist/src/shell/AuthoredHome.js +3 -1
- package/dist/src/shell/CodeTab.d.ts +10 -5
- package/dist/src/shell/CodeTab.js +44 -1
- package/dist/src/shell/ComponentPage.d.ts +5 -5
- package/dist/src/shell/ComponentPage.js +4 -2
- package/dist/src/shell/CustomControl.js +14 -21
- package/dist/src/shell/HomePage.js +5 -3
- package/dist/src/shell/PageWidth.d.ts +29 -0
- package/dist/src/shell/PageWidth.js +18 -0
- package/dist/src/shell/PropRows.d.ts +2 -12
- package/dist/src/shell/PropRows.js +6 -7
- package/dist/src/shell/PropsTab.js +2 -3
- package/dist/src/shell/Shell.js +5 -2
- package/dist/src/shell/StoryPicker.d.ts +4 -2
- package/dist/src/shell/StoryPicker.js +3 -3
- package/dist/src/shell/Switch.d.ts +29 -0
- package/dist/src/shell/Switch.js +11 -0
- package/dist/src/shell/customControls.d.ts +7 -0
- package/dist/src/shell/customControls.js +12 -1
- package/dist/src/shell/dockTabs.js +2 -1
- package/dist/src/shell/frameOrder.d.ts +35 -0
- package/dist/src/shell/frameOrder.js +45 -0
- package/dist/src/shell/laterStrings.d.ts +1 -0
- package/dist/src/shell/laterStrings.js +1 -0
- package/dist/src/shell/shell.css +36 -9
- package/dist/src/shell/useFrameQueue.d.ts +8 -3
- package/dist/src/shell/useFrameQueue.js +42 -12
- package/dist/src/shell/useScrollSettled.d.ts +18 -0
- package/dist/src/shell/useScrollSettled.js +56 -0
- package/dist/src/shell/valueEditors/ValueField.js +2 -1
- package/dist/src/storyCheck.d.ts +14 -0
- package/dist/src/storyCheck.js +16 -0
- package/dist/src/storyCodeText.d.ts +8 -2
- package/dist/src/storyCodeText.js +8 -5
- package/dist/src/storyControls.d.ts +3 -0
- package/dist/src/vite/agent.types.d.ts +8 -1
- package/dist/src/vite/agentRoutes.js +38 -10
- package/dist/src/vite/agentStoryCode.d.ts +3 -0
- package/dist/src/vite/agentStoryCode.js +8 -2
- package/dist/src/vite/argControlsConfig.d.ts +30 -0
- package/dist/src/vite/argControlsConfig.js +176 -0
- package/dist/src/vite/catalogueConfig.js +6 -1
- package/dist/src/vite/config.d.ts +38 -3
- package/dist/src/vite/configFile.d.ts +3 -2
- package/dist/src/vite/configFile.js +6 -21
- package/dist/src/vite/configKeys.js +2 -1
- package/dist/src/vite/forgebookPlugin.js +3 -1
- package/dist/src/vite/pages.js +12 -1
- package/dist/src/vite/propDocs.js +58 -3
- package/dist/src/vite/typedControls.d.ts +31 -0
- package/dist/src/vite/typedControls.js +66 -0
- package/package.json +2 -2
|
@@ -9,21 +9,11 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
9
9
|
*
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
|
-
import { useEffect, useRef, useState } from "react";
|
|
12
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
13
|
+
import { controlArgTypeOf } from "../controlCode.js";
|
|
13
14
|
import { isArgValue, UNSET_ARG } from "../messages.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
const argTypeOf = (prop) => ({
|
|
17
|
-
name: prop.name,
|
|
18
|
-
label: prop.label ?? prop.name,
|
|
19
|
-
description: prop.description,
|
|
20
|
-
...(prop.choices === undefined ? {} : { options: prop.choices }),
|
|
21
|
-
...(prop.min === undefined ? {} : { min: prop.min }),
|
|
22
|
-
...(prop.max === undefined ? {} : { max: prop.max }),
|
|
23
|
-
...(prop.step === undefined ? {} : { step: prop.step }),
|
|
24
|
-
// the frame's word, read here since only a control needs it: what is no plain data never reaches a control
|
|
25
|
-
...(prop.settings === undefined || !isArgValue(prop.settings) ? {} : { settings: prop.settings })
|
|
26
|
-
});
|
|
15
|
+
import { useCatalogue } from "./catalogueContext.js";
|
|
16
|
+
import { CONTROL_STRINGS, controlStringsOf, controlValueProblem, loadControl } from "./customControls.js";
|
|
27
17
|
/** The value as text, to tell whether it is still the one the control gave. */
|
|
28
18
|
const textOf = (value) => JSON.stringify(value) ?? "";
|
|
29
19
|
/** A value a control gave, checked: what to send, or what is wrong with it. */
|
|
@@ -40,15 +30,15 @@ const checkedChange = (module, next) => {
|
|
|
40
30
|
*
|
|
41
31
|
* @param props - the module, the value it starts from, and what it reports to.
|
|
42
32
|
*/
|
|
43
|
-
const ControlRoom = ({ module, name, value, argType, onAccepted, onProblem }) => {
|
|
33
|
+
const ControlRoom = ({ module, name, value, argType, strings, onAccepted, onProblem }) => {
|
|
44
34
|
const element = useRef(null);
|
|
45
|
-
const start = useRef({ value, argType, onAccepted, onProblem });
|
|
46
|
-
start.current = { value, argType, onAccepted, onProblem };
|
|
35
|
+
const start = useRef({ value, argType, strings, onAccepted, onProblem });
|
|
36
|
+
start.current = { value, argType, strings, onAccepted, onProblem };
|
|
47
37
|
useEffect(() => {
|
|
48
38
|
const host = element.current;
|
|
49
39
|
if (host === null)
|
|
50
40
|
return;
|
|
51
|
-
const { value: shown, argType: told, onProblem: say } = start.current;
|
|
41
|
+
const { value: shown, argType: told, strings: words, onProblem: say } = start.current;
|
|
52
42
|
const refusal = shown === undefined ? undefined : controlValueProblem(module, shown);
|
|
53
43
|
say(refusal === undefined ? undefined : CONTROL_STRINGS.controlValue(name, refusal));
|
|
54
44
|
let isMounted = true;
|
|
@@ -68,7 +58,8 @@ const ControlRoom = ({ module, name, value, argType, onAccepted, onProblem }) =>
|
|
|
68
58
|
start.current.onAccepted(change.value);
|
|
69
59
|
}
|
|
70
60
|
},
|
|
71
|
-
argType: told
|
|
61
|
+
argType: told,
|
|
62
|
+
strings: words
|
|
72
63
|
});
|
|
73
64
|
}
|
|
74
65
|
catch (error) {
|
|
@@ -100,8 +91,10 @@ const MountedControl = ({ module, prop, name, value, onValueChange }) => {
|
|
|
100
91
|
given.current = textOf(value);
|
|
101
92
|
setMounts(mounts + 1);
|
|
102
93
|
}
|
|
103
|
-
const argType =
|
|
104
|
-
|
|
94
|
+
const argType = controlArgTypeOf(prop);
|
|
95
|
+
const { strings: catalogueStrings } = useCatalogue();
|
|
96
|
+
const strings = useMemo(() => controlStringsOf(catalogueStrings, name), [catalogueStrings, name]);
|
|
97
|
+
return (_jsxs(_Fragment, { children: [_jsx(ControlRoom, { module: module, name: name, value: value, argType: argType, strings: strings, onProblem: setProblem, onAccepted: (next) => {
|
|
105
98
|
given.current = textOf(next);
|
|
106
99
|
onValueChange(prop.name, next ?? UNSET_ARG);
|
|
107
100
|
} }, `${mounts}|${JSON.stringify(argType)}`), problem === undefined ? null : (_jsx("p", { className: "forgebook-prop__description forgebook-prop__problem", role: "alert", children: problem }))] }));
|
|
@@ -4,6 +4,7 @@ import { findPage } from "../pageTree.js";
|
|
|
4
4
|
import { findComponent, findStory } from "../storyTree.js";
|
|
5
5
|
import { LATER_STRINGS } from "./laterStrings.js";
|
|
6
6
|
import { PackageHtml } from "./PackageHtml.js";
|
|
7
|
+
import { usePageWidth } from "./PageWidth.js";
|
|
7
8
|
import { PAGE_STRINGS } from "./pageStrings.js";
|
|
8
9
|
import { useLoaded } from "./useLoaded.js";
|
|
9
10
|
/** The most components a family's card lists before "Show all". */
|
|
@@ -128,7 +129,8 @@ export const HomePage = ({ title, tree, sources, storyGlobs = [], recent, hrefOf
|
|
|
128
129
|
// it stops on a pattern that matches nothing
|
|
129
130
|
const listingGlobs = storyGlobs.filter((glob) => !glob.startsWith("!") || glob.startsWith("!("));
|
|
130
131
|
const isWaiting = listingGlobs.length > 0 && componentCount(tree) === 0;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
const pageWidth = usePageWidth();
|
|
133
|
+
return (_jsxs("div", { className: "forgebook-workspace", children: [header, _jsxs("div", { className: "forgebook-scroll", children: [_jsx("div", { className: "forgebook-home__tools", children: pageWidth.toggle }), _jsxs("div", { className: "forgebook-home", "data-page-width": pageWidth.width, children: [_jsxs("header", { className: "forgebook-home__head", children: [_jsx("h1", { className: "forgebook-home__title", children: title }), _jsxs("p", { className: "forgebook-doc__lead", children: [isWaiting ? PAGE_STRINGS.homeNoStoryTitle : null, !isWaiting && tree.sources.length === 0 ? PAGE_STRINGS.homeEmpty : null, !isWaiting && tree.sources.length > 0
|
|
134
|
+
? PAGE_STRINGS.homeSummary(componentCount(tree), libraryCount(tree))
|
|
135
|
+
: null] })] }), isWaiting ? _jsx(NoStoryYet, { globs: listingGlobs }) : null, _jsx(RecentSection, { tree: tree, recent: recent, hrefOf: hrefOf, onOpen: onOpen }), tree.sources.map((source) => (_jsx(LibrarySection, { source: source, sources: sources, hrefOf: hrefOf, onOpen: onOpen }, source.id)))] })] })] }));
|
|
134
136
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The width of the pages that list stories, the home and a component's page: contained in a centred column, or the
|
|
3
|
+
* whole width of the page; a reader's choice kept in their browser.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* It loads with those pages, never with the first screen: the shell's header keeps an empty place of the button's size
|
|
7
|
+
* beside the stories' picker (`.forgebook-page-width`), which the component's page fills with the button, so the
|
|
8
|
+
* picker stays where it is between a component's page, while its code arrives, and its stories' pages. A story's page
|
|
9
|
+
* has its own width controls on its stage. Each page writes the width on its column (`data-page-width`), where
|
|
10
|
+
* `shell.scss` reads it.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import type { ReactNode } from "react";
|
|
15
|
+
/** How wide the pages that list stories are: in a centred column, or the whole page. */
|
|
16
|
+
export type PageWidth = "contained" | "full";
|
|
17
|
+
/** The reader's page width and the button that switches it. */
|
|
18
|
+
export type PageWidthState = {
|
|
19
|
+
/** The width, for the column's `data-page-width`. */
|
|
20
|
+
readonly width: PageWidth;
|
|
21
|
+
/** The button, pressed while the page is contained. */
|
|
22
|
+
readonly toggle: ReactNode;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The reader's page width, kept for the next visit, and its button.
|
|
26
|
+
*
|
|
27
|
+
* @returns the width and the button.
|
|
28
|
+
*/
|
|
29
|
+
export declare const usePageWidth: () => PageWidthState;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Icon } from "./icons.js";
|
|
3
|
+
import { LATER_STRINGS } from "./laterStrings.js";
|
|
4
|
+
import { useStored } from "./useStored.js";
|
|
5
|
+
/** The width a stored text holds: contained unless the reader chose the whole page. */
|
|
6
|
+
const storedWidth = (stored) => (stored === "full" ? "full" : "contained");
|
|
7
|
+
/**
|
|
8
|
+
* The reader's page width, kept for the next visit, and its button.
|
|
9
|
+
*
|
|
10
|
+
* @returns the width and the button.
|
|
11
|
+
*/
|
|
12
|
+
export const usePageWidth = () => {
|
|
13
|
+
const [width, setWidth] = useStored("page-width", storedWidth, String);
|
|
14
|
+
return {
|
|
15
|
+
width,
|
|
16
|
+
toggle: (_jsx("button", { type: "button", className: "forgebook-button forgebook-button--icon forgebook-page-width", title: LATER_STRINGS.pageContained, "aria-label": LATER_STRINGS.pageContained, "aria-pressed": width === "contained", onClick: () => setWidth(width === "full" ? "contained" : "full"), children: _jsx(Icon, { name: "width", size: 14 }) }))
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
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,
|
|
3
|
+
* group, its type), then its control, or its description for reading.
|
|
4
4
|
*
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
@@ -26,7 +26,7 @@ export type FieldState = {
|
|
|
26
26
|
/** Called with a changed value. */
|
|
27
27
|
readonly onValueChange: OnValueChange;
|
|
28
28
|
};
|
|
29
|
-
/** One adjustable prop with its control. */
|
|
29
|
+
/** One adjustable prop with its control: a boolean's is a switch. */
|
|
30
30
|
export declare const PropControl: ({ prop, showsGroup, value, panelValue, named, namedValues, loadTokens, argControls, textArg, onValueChange }: {
|
|
31
31
|
readonly prop: PanelProp;
|
|
32
32
|
/** Whether it names its group: in the "Required" group. */
|
|
@@ -46,16 +46,6 @@ export declare const PropControl: ({ prop, showsGroup, value, panelValue, named,
|
|
|
46
46
|
readonly textArg: TextArgNote | undefined;
|
|
47
47
|
readonly onValueChange: OnValueChange;
|
|
48
48
|
}) => ReactNode;
|
|
49
|
-
/** A toggle: its name in a compact checkbox, its description as the tooltip and for screen readers. */
|
|
50
|
-
export declare const Toggle: ({ prop, showsGroup, value, panelValue, onValueChange }: {
|
|
51
|
-
readonly prop: PanelProp;
|
|
52
|
-
/** Whether it names its group: in the "Required" group. */
|
|
53
|
-
readonly showsGroup: boolean;
|
|
54
|
-
readonly value: ArgValue | undefined;
|
|
55
|
-
/** The panel's own value; none when the story's own is shown. */
|
|
56
|
-
readonly panelValue: ArgValue | undefined;
|
|
57
|
-
readonly onValueChange: OnValueChange;
|
|
58
|
-
}) => ReactNode;
|
|
59
49
|
/** A prop the panel cannot set: its name, its type and its description, for reading; its group when asked. */
|
|
60
50
|
export declare const ReadOnlyProp: ({ prop, showsGroup }: {
|
|
61
51
|
readonly prop: PanelProp;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
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,
|
|
4
|
+
* group, its type), then its control, or its description for reading.
|
|
5
5
|
*
|
|
6
6
|
* @module
|
|
7
7
|
*/
|
|
@@ -11,6 +11,7 @@ import { isUnsetArg, UNSET_ARG } from "../messages.js";
|
|
|
11
11
|
import { Icon } from "./icons.js";
|
|
12
12
|
import { LazyView, lazyModule } from "./lazyModule.js";
|
|
13
13
|
import { ChoiceControl, MOST_PLAIN_OPTIONS, NumberField, textOf } from "./PropFields.js";
|
|
14
|
+
import { Switch } from "./Switch.js";
|
|
14
15
|
import { STRINGS } from "./strings.js";
|
|
15
16
|
/** The editors a catalogue's plain props never load: nested fields, a searchable list, the tokens, the files. */
|
|
16
17
|
const PROP_EDITOR = lazyModule(() => import("./valueEditors/PropEditor.js").then((module) => module.PropEditor));
|
|
@@ -60,12 +61,15 @@ const usesEditors = (prop, named) => named !== undefined ||
|
|
|
60
61
|
(prop.kind === "choice" &&
|
|
61
62
|
(prop.control === undefined || prop.control === "select") &&
|
|
62
63
|
(prop.choices?.length ?? 0) > MOST_PLAIN_OPTIONS);
|
|
63
|
-
/** One adjustable prop with its control. */
|
|
64
|
+
/** One adjustable prop with its control: a boolean's is a switch. */
|
|
64
65
|
export const PropControl = ({ prop, showsGroup, value, panelValue, named, namedValues, loadTokens, argControls, textArg, onValueChange }) => {
|
|
65
66
|
// the description is shown under the control and named by it, so the control's name is the prop alone
|
|
66
67
|
const descriptionId = useId();
|
|
67
68
|
const label = prop.label ?? prop.name;
|
|
68
69
|
const state = { value, panelValue, onValueChange };
|
|
70
|
+
if (prop.kind === "toggle") {
|
|
71
|
+
return (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup, state: state }), _jsx("div", { className: "forgebook-prop__field", children: _jsx(Switch, { label: label, isOn: value === true, describedBy: descriptionId, onChange: (isOn) => onValueChange(prop.name, isOn) }) }), _jsx(PropNote, { id: descriptionId, prop: prop })] }));
|
|
72
|
+
}
|
|
69
73
|
if (usesEditors(prop, named)) {
|
|
70
74
|
const editorProps = {
|
|
71
75
|
prop,
|
|
@@ -90,10 +94,5 @@ export const PropControl = ({ prop, showsGroup, value, panelValue, named, namedV
|
|
|
90
94
|
const TextControl = isMultiline ? "textarea" : "input";
|
|
91
95
|
return (_jsxs("div", { className: "forgebook-prop", children: [_jsx(PropHead, { prop: prop, showsGroup: showsGroup, state: state }), _jsxs("div", { className: "forgebook-prop__field", children: [_jsx(TextControl, { "aria-label": label, "aria-describedby": descriptionId, ...(isMultiline ? { rows: 4 } : { type: "text" }), value: textOf(value), onChange: (event) => onValueChange(prop.name, event.target.value) }), 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 })] }));
|
|
92
96
|
};
|
|
93
|
-
/** A toggle: its name in a compact checkbox, its description as the tooltip and for screen readers. */
|
|
94
|
-
export const Toggle = ({ prop, showsGroup, value, panelValue, onValueChange }) => {
|
|
95
|
-
const descriptionId = useId();
|
|
96
|
-
return (_jsxs("span", { className: "forgebook-toggle-row", children: [_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 })] }), isUnsetArg(panelValue) ? _jsx("span", { className: "forgebook-prop__state", children: STRINGS.stateUnset }) : null, _jsx(FieldActions, { prop: prop, value: value, panelValue: panelValue, onValueChange: onValueChange })] }));
|
|
97
|
-
};
|
|
98
97
|
/** A prop the panel cannot set: its name, its type and its description, for reading; its group when asked. */
|
|
99
98
|
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 })] }));
|
|
@@ -3,7 +3,7 @@ import { isNamedProp, settableProps } from "../argTypes.js";
|
|
|
3
3
|
import { canOrderRequired, laidOutProps, propGroupOrder } from "../propGroups.js";
|
|
4
4
|
import { useCatalogue } from "./catalogueContext.js";
|
|
5
5
|
import { Icon } from "./icons.js";
|
|
6
|
-
import { PropControl, ReadOnlyProp
|
|
6
|
+
import { PropControl, ReadOnlyProp } from "./PropRows.js";
|
|
7
7
|
import { RequiredFirstSwitch } from "./RequiredFirstSwitch.js";
|
|
8
8
|
import { STRINGS } from "./strings.js";
|
|
9
9
|
import { useRequiredFirst } from "./useRequiredFirst.js";
|
|
@@ -45,8 +45,7 @@ export const PropsTab = ({ props, values, panelArgs, loadTokens, argControls, co
|
|
|
45
45
|
const ungrouped = propGroups.find((group) => group.name === undefined)?.props ?? [];
|
|
46
46
|
const settable = ungrouped.filter(isSettable);
|
|
47
47
|
const control = (prop, showsGroup) => (_jsx(PropControl, { prop: prop, showsGroup: showsGroup, value: values[prop.name], panelValue: panelArgs[prop.name], named: isNamed(prop) ? namedValues[prop.type] : undefined, namedValues: namedValues, loadTokens: loadTokens, argControls: argControls, textArg: textArgs[prop.name], onValueChange: onValueChange }, prop.name));
|
|
48
|
-
const field = (prop, showsGroup) => prop.kind === "toggle" ? (_jsx(Toggle, { prop: prop, showsGroup: showsGroup, value: values[prop.name], panelValue: panelArgs[prop.name], onValueChange: onValueChange }, prop.name)) : (control(prop, showsGroup));
|
|
49
48
|
const others = ungrouped.filter((prop) => !isSettable(prop));
|
|
50
49
|
const hasControls = controls === "all" || controls.length > 0;
|
|
51
|
-
return (_jsxs("div", { className: "forgebook-tab-props", children: [canOrderRequired(listed) ? (_jsx("div", { className: "forgebook-tab-head", children: _jsx(RequiredFirstSwitch, { state: ordering }) })) : null, hasControls ? null : _jsx("p", { className: "forgebook-tab-note", children: STRINGS.fixedExamples }), required.length === 0 ? null : (_jsx(NamedGroup, { name: STRINGS.required, props: required, isSettable: isSettable, field:
|
|
50
|
+
return (_jsxs("div", { className: "forgebook-tab-props", children: [canOrderRequired(listed) ? (_jsx("div", { className: "forgebook-tab-head", children: _jsx(RequiredFirstSwitch, { state: ordering }) })) : null, hasControls ? null : _jsx("p", { className: "forgebook-tab-note", children: STRINGS.fixedExamples }), required.length === 0 ? null : (_jsx(NamedGroup, { name: STRINGS.required, props: required, isSettable: isSettable, field: control, isRequired: true })), settable.length === 0 ? null : (_jsx("div", { className: "forgebook-prop-group", children: _jsx("div", { className: "forgebook-prop-grid", children: settable.map((prop) => control(prop, false)) }) })), others.length === 0 ? null : (_jsxs("div", { className: "forgebook-prop-group", children: [_jsx("h3", { className: "forgebook-prop-group__title", children: hasControls ? STRINGS.otherProps : STRINGS.allProps }), _jsx("div", { className: "forgebook-prop-grid", children: others.map((prop) => (_jsx(ReadOnlyProp, { prop: prop, showsGroup: false }, prop.name))) })] })), propGroups.map((group) => group.name === undefined ? null : (_jsx(NamedGroup, { name: group.name, props: group.props, isSettable: isSettable, field: control }, group.name))), hasControls ? (_jsxs("div", { className: "forgebook-tab-foot", children: [_jsx("button", { type: "button", className: "forgebook-text-button", onClick: onReset, children: STRINGS.resetArgs }), worstCase?.canApply === true ? _jsx(WorstCaseControl, { worstCase: worstCase }) : null] })) : null] }));
|
|
52
51
|
};
|
package/dist/src/shell/Shell.js
CHANGED
|
@@ -582,7 +582,10 @@ const ShellPage = ({ title, logo, hasBranding, heights, sources, storyGlobs, fra
|
|
|
582
582
|
const files = source === undefined ? undefined : sourceFiles(source);
|
|
583
583
|
const storyHrefOf = (story) => addressQuery(pageAddress(storyPage(story)), catalogue);
|
|
584
584
|
const columnHeightOf = (storyId) => knownHeight(storyHeights, storyId, drawnWidth(address.view.width, PAGE_COLUMN_WIDTH));
|
|
585
|
-
|
|
585
|
+
// the component's page puts its width's button in the header (`PageWidth.tsx`); until then, and on a story's page,
|
|
586
|
+
// an empty place of the button's size keeps the picker where it stays
|
|
587
|
+
const headerWith = (tools) => (_jsx(ShellHeader, { problems: problems, location: location, page: shownPageLocation, isLogic: isLogicPage(shown), message: shown.kind === "missing" ? shown.message : undefined, picker: location === undefined ? null : (_jsx(StoryPicker, { component: location.component, currentStoryId: shownStoryId, onOpenComponent: () => openPage(componentPage(location.component)), onOpenStory: (story) => openPage(storyPage(story)), children: tools })), related: _jsx(RelatedLinks, { related: relatedComponents(tree, location), hrefOf: (page) => addressQuery(pageAddress(page), catalogue), onOpen: openPage }) }));
|
|
588
|
+
const header = headerWith(_jsx("span", { className: "forgebook-page-width" }));
|
|
586
589
|
const homeProps = {
|
|
587
590
|
title,
|
|
588
591
|
tree,
|
|
@@ -625,7 +628,7 @@ const ShellPage = ({ title, logo, hasBranding, heights, sources, storyGlobs, fra
|
|
|
625
628
|
...componentLoaders(files, shown.location.component),
|
|
626
629
|
background: storyBackground(shown.location.source.background, address.view, catalogue, isSystemDark),
|
|
627
630
|
heightOf: columnHeightOf,
|
|
628
|
-
|
|
631
|
+
headerWith,
|
|
629
632
|
scroll,
|
|
630
633
|
usedIn: usedInLinks(source, catalogue.hasUsedIn, tree, {
|
|
631
634
|
hrefOf: (page) => addressQuery(pageAddress(page), catalogue),
|
|
@@ -16,10 +16,12 @@ export type StoryPickerProps = {
|
|
|
16
16
|
readonly onOpenComponent: () => void;
|
|
17
17
|
/** Opens a story's page. */
|
|
18
18
|
readonly onOpenStory: (story: TreeStory) => void;
|
|
19
|
+
/** What sits beside the select, at the header's end: the page width's button. */
|
|
20
|
+
readonly children?: ReactNode;
|
|
19
21
|
};
|
|
20
22
|
/**
|
|
21
23
|
* The picker of a component's stories.
|
|
22
24
|
*
|
|
23
|
-
* @param props - the component, the shown story
|
|
25
|
+
* @param props - the component, the shown story, how to open a page, and what sits beside the select.
|
|
24
26
|
*/
|
|
25
|
-
export declare const StoryPicker: ({ component, currentStoryId, onOpenComponent, onOpenStory }: StoryPickerProps) => ReactNode;
|
|
27
|
+
export declare const StoryPicker: ({ component, currentStoryId, onOpenComponent, onOpenStory, children }: StoryPickerProps) => ReactNode;
|
|
@@ -15,9 +15,9 @@ const ALL = "";
|
|
|
15
15
|
/**
|
|
16
16
|
* The picker of a component's stories.
|
|
17
17
|
*
|
|
18
|
-
* @param props - the component, the shown story
|
|
18
|
+
* @param props - the component, the shown story, how to open a page, and what sits beside the select.
|
|
19
19
|
*/
|
|
20
|
-
export const StoryPicker = ({ component, currentStoryId, onOpenComponent, onOpenStory }) => {
|
|
20
|
+
export const StoryPicker = ({ component, currentStoryId, onOpenComponent, onOpenStory, children }) => {
|
|
21
21
|
const id = useId();
|
|
22
22
|
const { storyGroups } = useCatalogue();
|
|
23
23
|
const sections = groupedStories(component.stories, storyGroups.map((group) => group.id));
|
|
@@ -27,5 +27,5 @@ export const StoryPicker = ({ component, currentStoryId, onOpenComponent, onOpen
|
|
|
27
27
|
onOpenComponent();
|
|
28
28
|
else
|
|
29
29
|
onOpenStory(chosen);
|
|
30
|
-
}, children: [_jsx("option", { value: ALL, children: STRINGS.allStories(component.stories.length) }), sections.map((section) => section.group === undefined ? (section.stories.map((story) => (_jsx("option", { value: story.id, children: story.name }, story.id)))) : (_jsx("optgroup", { label: groupLabel(storyGroups, section.group), children: section.stories.map((story) => (_jsx("option", { value: story.id, children: story.name }, story.id))) }, section.group)))] })] }));
|
|
30
|
+
}, children: [_jsx("option", { value: ALL, children: STRINGS.allStories(component.stories.length) }), sections.map((section) => section.group === undefined ? (section.stories.map((story) => (_jsx("option", { value: story.id, children: story.name }, story.id)))) : (_jsx("optgroup", { label: groupLabel(storyGroups, section.group), children: section.stories.map((story) => (_jsx("option", { value: story.id, children: story.name }, story.id))) }, section.group)))] }), children] }));
|
|
31
31
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A boolean's switch, in the Props tab's fields and in the nested fields of the value editors.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* A native checkbox drawn as a switch (`role="switch"`, `.forgebook-switch`): it keeps Space, the label and the
|
|
6
|
+
* disabled state of a checkbox, and Enter turns it as well, as a switch's button would.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import type { ReactNode } from "react";
|
|
11
|
+
/** What a switch shows and calls. */
|
|
12
|
+
export type SwitchProps = {
|
|
13
|
+
/** Its accessible name. */
|
|
14
|
+
readonly label: string;
|
|
15
|
+
/** Whether it is on. */
|
|
16
|
+
readonly isOn: boolean;
|
|
17
|
+
/** The id of the text that describes it, when there is one. */
|
|
18
|
+
readonly describedBy?: string | undefined;
|
|
19
|
+
/** Whether it cannot be turned: a value the type fixes. */
|
|
20
|
+
readonly disabled?: boolean | undefined;
|
|
21
|
+
/** Called with the new state. */
|
|
22
|
+
readonly onChange: (isOn: boolean) => void;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The switch.
|
|
26
|
+
*
|
|
27
|
+
* @param props - its name, state and setter.
|
|
28
|
+
*/
|
|
29
|
+
export declare const Switch: ({ label, isOn, describedBy, disabled, onChange }: SwitchProps) => ReactNode;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The switch.
|
|
4
|
+
*
|
|
5
|
+
* @param props - its name, state and setter.
|
|
6
|
+
*/
|
|
7
|
+
export const Switch = ({ label, isOn, describedBy, disabled, onChange }) => (_jsx("input", { type: "checkbox", role: "switch", className: "forgebook-switch", "aria-label": label, "aria-checked": isOn, "aria-describedby": describedBy, checked: isOn, disabled: disabled, onChange: (event) => onChange(event.target.checked), onKeyDown: (event) => {
|
|
8
|
+
// a click turns it as Space does, through its change
|
|
9
|
+
if (event.key === "Enter")
|
|
10
|
+
event.currentTarget.click();
|
|
11
|
+
} }));
|
|
@@ -53,3 +53,10 @@ export declare const controlValueProblem: (module: ArgControlModule, value: ArgV
|
|
|
53
53
|
* @param loaders - the catalogue's custom controls.
|
|
54
54
|
*/
|
|
55
55
|
export declare const customChecksOf: (names: readonly string[], loaders: ArgControlLoaders) => Promise<Readonly<Record<string, (value: ArgValue) => string | undefined>>>;
|
|
56
|
+
/**
|
|
57
|
+
* A control's texts as the catalogue words them: its `strings` under `argControls.<name>.`, by the rest of the key.
|
|
58
|
+
*
|
|
59
|
+
* @param strings - the catalogue's replaced texts.
|
|
60
|
+
* @param name - the control's name.
|
|
61
|
+
*/
|
|
62
|
+
export declare const controlStringsOf: (strings: Readonly<Record<string, string>>, name: string) => Readonly<Record<string, string>>;
|
|
@@ -16,7 +16,8 @@ const isControlModule = (value) => typeof value === "object" &&
|
|
|
16
16
|
"mount" in value &&
|
|
17
17
|
typeof value.mount === "function" &&
|
|
18
18
|
"schema" in value &&
|
|
19
|
-
isValueSchema(value.schema)
|
|
19
|
+
isValueSchema(value.schema) &&
|
|
20
|
+
(!("toCode" in value) || value.toCode === undefined || typeof value.toCode === "function");
|
|
20
21
|
/** Each control's load, kept: every field and check of a page share one. */
|
|
21
22
|
const loads = new Map();
|
|
22
23
|
/** Loads one control's module, once, and reads what it exports. */
|
|
@@ -68,3 +69,13 @@ export const customChecksOf = async (names, loaders) => {
|
|
|
68
69
|
? [[name, (value) => controlValueProblem(control.module, value)]]
|
|
69
70
|
: []));
|
|
70
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* A control's texts as the catalogue words them: its `strings` under `argControls.<name>.`, by the rest of the key.
|
|
74
|
+
*
|
|
75
|
+
* @param strings - the catalogue's replaced texts.
|
|
76
|
+
* @param name - the control's name.
|
|
77
|
+
*/
|
|
78
|
+
export const controlStringsOf = (strings, name) => {
|
|
79
|
+
const prefix = `argControls.${name}.`;
|
|
80
|
+
return Object.fromEntries(Object.entries(strings).flatMap(([key, text]) => key.startsWith(prefix) && key.length > prefix.length ? [[key.slice(prefix.length), text]] : []));
|
|
81
|
+
};
|
|
@@ -168,7 +168,8 @@ const codeTabs = (input) => {
|
|
|
168
168
|
exportName: input.story.exportName,
|
|
169
169
|
componentName: docs.status === "ready" ? codeNameOf(docs.value) : input.componentName,
|
|
170
170
|
args: input.values,
|
|
171
|
-
edited: Object.keys(input.panelArgs)
|
|
171
|
+
edited: Object.keys(input.panelArgs),
|
|
172
|
+
panel: input
|
|
172
173
|
} }))
|
|
173
174
|
}
|
|
174
175
|
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which waiting frames of a page start first: the ones the reader is looking at, then their neighbours outward.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* A page lists its stories top to bottom, but the reader may be anywhere on it: after a scroll to the end, the stories
|
|
6
|
+
* in view must not wait behind the ones above them that are still near. Read by `useFrameQueue`.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/** A box's vertical extent, in the same coordinates as the visible part of the page. */
|
|
11
|
+
export type VerticalBox = {
|
|
12
|
+
/** Its top edge, in CSS px. */
|
|
13
|
+
readonly top: number;
|
|
14
|
+
/** Its bottom edge, in CSS px. */
|
|
15
|
+
readonly bottom: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* How far a box is from the visible part of the page: 0 when any of it is in view, otherwise the gap to the nearest
|
|
19
|
+
* edge.
|
|
20
|
+
*
|
|
21
|
+
* @param box - the box of a story's frame.
|
|
22
|
+
* @param view - the visible part of the page.
|
|
23
|
+
* @returns The distance, in CSS px.
|
|
24
|
+
*/
|
|
25
|
+
export declare const distanceFromView: (box: VerticalBox, view: VerticalBox) => number;
|
|
26
|
+
/**
|
|
27
|
+
* The frames to start now: the nearest to the visible part first, in the page's order among equals, at most `free`.
|
|
28
|
+
*
|
|
29
|
+
* @param waiting - the stories waiting for their frame, in the page's order.
|
|
30
|
+
* @param distances - each story's distance from the visible part, in CSS px; a story with none comes after every
|
|
31
|
+
* other.
|
|
32
|
+
* @param free - how many frames may start.
|
|
33
|
+
* @returns The stories to start, nearest first.
|
|
34
|
+
*/
|
|
35
|
+
export declare const nearestFrames: (waiting: readonly string[], distances: ReadonlyMap<string, number>, free: number) => string[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which waiting frames of a page start first: the ones the reader is looking at, then their neighbours outward.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* A page lists its stories top to bottom, but the reader may be anywhere on it: after a scroll to the end, the stories
|
|
6
|
+
* in view must not wait behind the ones above them that are still near. Read by `useFrameQueue`.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* How far a box is from the visible part of the page: 0 when any of it is in view, otherwise the gap to the nearest
|
|
12
|
+
* edge.
|
|
13
|
+
*
|
|
14
|
+
* @param box - the box of a story's frame.
|
|
15
|
+
* @param view - the visible part of the page.
|
|
16
|
+
* @returns The distance, in CSS px.
|
|
17
|
+
*/
|
|
18
|
+
export const distanceFromView = (box, view) => {
|
|
19
|
+
if (box.bottom <= view.top)
|
|
20
|
+
return view.top - box.bottom;
|
|
21
|
+
if (box.top >= view.bottom)
|
|
22
|
+
return box.top - view.bottom;
|
|
23
|
+
return 0;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The frames to start now: the nearest to the visible part first, in the page's order among equals, at most `free`.
|
|
27
|
+
*
|
|
28
|
+
* @param waiting - the stories waiting for their frame, in the page's order.
|
|
29
|
+
* @param distances - each story's distance from the visible part, in CSS px; a story with none comes after every
|
|
30
|
+
* other.
|
|
31
|
+
* @param free - how many frames may start.
|
|
32
|
+
* @returns The stories to start, nearest first.
|
|
33
|
+
*/
|
|
34
|
+
export const nearestFrames = (waiting, distances, free) => {
|
|
35
|
+
const distanceOf = (storyId) => distances.get(storyId) ?? Number.POSITIVE_INFINITY;
|
|
36
|
+
// a stable sort keeps the page's order among stories at the same distance, the demo first among those in view
|
|
37
|
+
// (compared rather than subtracted: two stories with no distance would give NaN)
|
|
38
|
+
const byDistance = (first, second) => {
|
|
39
|
+
const [one, other] = [distanceOf(first), distanceOf(second)];
|
|
40
|
+
if (one === other)
|
|
41
|
+
return 0;
|
|
42
|
+
return one < other ? -1 : 1;
|
|
43
|
+
};
|
|
44
|
+
return [...waiting].sort(byDistance).slice(0, Math.max(0, free));
|
|
45
|
+
};
|
|
@@ -121,6 +121,7 @@ export const LATER_STRINGS = lateStrings({
|
|
|
121
121
|
backToPlace: "Back to its place",
|
|
122
122
|
backToPlaceNamed: (name) => `Back to its place: ${name}`,
|
|
123
123
|
examples: "Examples",
|
|
124
|
+
pageContained: "Contained width",
|
|
124
125
|
pageError: "The component's page could not be read",
|
|
125
126
|
droppedArgs: "The link sets values this story does not take; they were left out:",
|
|
126
127
|
droppedArgReasons: {
|
package/dist/src/shell/shell.css
CHANGED
|
@@ -624,6 +624,9 @@
|
|
|
624
624
|
min-height: 0;
|
|
625
625
|
overflow: auto;
|
|
626
626
|
}
|
|
627
|
+
.forgebook-dock__body:has(.forgebook-tab-foot) {
|
|
628
|
+
scroll-padding-block-end: 2.5rem;
|
|
629
|
+
}
|
|
627
630
|
.forgebook-icon-button {
|
|
628
631
|
display: inline-flex;
|
|
629
632
|
align-self: center;
|
|
@@ -667,10 +670,14 @@
|
|
|
667
670
|
color: var(--forgebook-text-secondary);
|
|
668
671
|
}
|
|
669
672
|
.forgebook-tab-foot {
|
|
673
|
+
position: sticky;
|
|
674
|
+
bottom: 0;
|
|
675
|
+
z-index: 1;
|
|
670
676
|
display: flex;
|
|
671
677
|
align-items: center;
|
|
672
678
|
gap: 0.75rem;
|
|
673
679
|
padding: 0.625rem 0.875rem;
|
|
680
|
+
background: var(--forgebook-surface);
|
|
674
681
|
box-shadow: inset 0 1px 0 var(--forgebook-separator);
|
|
675
682
|
}
|
|
676
683
|
.forgebook-tab-foot__note {
|
|
@@ -1554,6 +1561,21 @@
|
|
|
1554
1561
|
.forgebook-picker__select {
|
|
1555
1562
|
max-width: 22rem;
|
|
1556
1563
|
}
|
|
1564
|
+
.forgebook-page-width {
|
|
1565
|
+
flex: none;
|
|
1566
|
+
width: 1.625rem;
|
|
1567
|
+
}
|
|
1568
|
+
.forgebook-home__tools {
|
|
1569
|
+
display: flex;
|
|
1570
|
+
justify-content: flex-end;
|
|
1571
|
+
height: 0;
|
|
1572
|
+
padding: 0 2rem;
|
|
1573
|
+
}
|
|
1574
|
+
.forgebook-home__tools > * {
|
|
1575
|
+
position: relative;
|
|
1576
|
+
z-index: 1;
|
|
1577
|
+
margin-top: 2rem;
|
|
1578
|
+
}
|
|
1557
1579
|
.forgebook-header__title {
|
|
1558
1580
|
margin: 0;
|
|
1559
1581
|
font: var(--forgebook-text-display);
|
|
@@ -1991,6 +2013,13 @@
|
|
|
1991
2013
|
.forgebook-doc[data-audience=designer] {
|
|
1992
2014
|
max-width: 72rem;
|
|
1993
2015
|
}
|
|
2016
|
+
[data-page-width] {
|
|
2017
|
+
transition: max-width 260ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
2018
|
+
}
|
|
2019
|
+
.forgebook-doc[data-page-width=full],
|
|
2020
|
+
.forgebook-home[data-page-width=full] {
|
|
2021
|
+
max-width: 100%;
|
|
2022
|
+
}
|
|
1994
2023
|
.forgebook-doc__lead {
|
|
1995
2024
|
margin: 0;
|
|
1996
2025
|
font-size: 1.0625rem;
|
|
@@ -2622,6 +2651,9 @@
|
|
|
2622
2651
|
.forgebook-stage-tools {
|
|
2623
2652
|
animation-name: forgebook-appear;
|
|
2624
2653
|
}
|
|
2654
|
+
[data-page-width] {
|
|
2655
|
+
transition: none;
|
|
2656
|
+
}
|
|
2625
2657
|
.forgebook-sidebar,
|
|
2626
2658
|
.forgebook-sidebar[data-overlay],
|
|
2627
2659
|
.forgebook-sidebar-shade {
|
|
@@ -2682,7 +2714,7 @@
|
|
|
2682
2714
|
gap: 0.375rem;
|
|
2683
2715
|
}
|
|
2684
2716
|
.forgebook-prop select,
|
|
2685
|
-
.forgebook-prop input,
|
|
2717
|
+
.forgebook-prop input:not(.forgebook-switch),
|
|
2686
2718
|
.forgebook-prop textarea {
|
|
2687
2719
|
box-sizing: border-box;
|
|
2688
2720
|
height: 1.625rem;
|
|
@@ -2695,7 +2727,7 @@
|
|
|
2695
2727
|
color: var(--forgebook-text);
|
|
2696
2728
|
}
|
|
2697
2729
|
.forgebook-prop select:focus-visible,
|
|
2698
|
-
.forgebook-prop input:focus-visible,
|
|
2730
|
+
.forgebook-prop input:not(.forgebook-switch):focus-visible,
|
|
2699
2731
|
.forgebook-prop textarea:focus-visible {
|
|
2700
2732
|
outline: 2px solid var(--forgebook-focus-halo);
|
|
2701
2733
|
outline-offset: 0;
|
|
@@ -2708,7 +2740,7 @@
|
|
|
2708
2740
|
padding: 0.25rem 0.5rem;
|
|
2709
2741
|
resize: vertical;
|
|
2710
2742
|
}
|
|
2711
|
-
.forgebook-prop input[type=checkbox],
|
|
2743
|
+
.forgebook-prop input[type=checkbox]:not(.forgebook-switch),
|
|
2712
2744
|
.forgebook-prop input[type=radio] {
|
|
2713
2745
|
height: auto;
|
|
2714
2746
|
box-shadow: none;
|
|
@@ -2755,11 +2787,6 @@
|
|
|
2755
2787
|
font: var(--forgebook-text-caption);
|
|
2756
2788
|
color: var(--forgebook-text-tertiary);
|
|
2757
2789
|
}
|
|
2758
|
-
.forgebook-toggle-row {
|
|
2759
|
-
display: inline-flex;
|
|
2760
|
-
align-items: center;
|
|
2761
|
-
gap: 0.25rem;
|
|
2762
|
-
}
|
|
2763
2790
|
.forgebook-prop__head {
|
|
2764
2791
|
display: flex;
|
|
2765
2792
|
align-items: center;
|
|
@@ -2776,7 +2803,7 @@
|
|
|
2776
2803
|
display: flex;
|
|
2777
2804
|
gap: 0.375rem;
|
|
2778
2805
|
}
|
|
2779
|
-
.forgebook-prop__field > input:first-child {
|
|
2806
|
+
.forgebook-prop__field > input:first-child:not(.forgebook-switch) {
|
|
2780
2807
|
flex: 1;
|
|
2781
2808
|
min-width: 0;
|
|
2782
2809
|
}
|