@goodbones/browser 0.1.0-beta.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/LICENSE +21 -0
- package/build/dts/index.d.ts +12 -0
- package/build/dts/index.d.ts.map +1 -0
- package/build/dts/main.d.ts +3 -0
- package/build/dts/main.d.ts.map +1 -0
- package/build/dts/model/atlas.d.ts +157 -0
- package/build/dts/model/atlas.d.ts.map +1 -0
- package/build/dts/model/campaigns.d.ts +141 -0
- package/build/dts/model/campaigns.d.ts.map +1 -0
- package/build/dts/server/collect.d.ts +16 -0
- package/build/dts/server/collect.d.ts.map +1 -0
- package/build/dts/server/compose.d.ts +13 -0
- package/build/dts/server/compose.d.ts.map +1 -0
- package/build/dts/server/export.d.ts +3 -0
- package/build/dts/server/export.d.ts.map +1 -0
- package/build/dts/server/handler.d.ts +6 -0
- package/build/dts/server/handler.d.ts.map +1 -0
- package/build/dts/server/serve.d.ts +15 -0
- package/build/dts/server/serve.d.ts.map +1 -0
- package/build/dts/server/site.d.ts +2 -0
- package/build/dts/server/site.d.ts.map +1 -0
- package/build/dts/server/source.d.ts +19 -0
- package/build/dts/server/source.d.ts.map +1 -0
- package/build/dts/server/vite-plugin.d.ts +8 -0
- package/build/dts/server/vite-plugin.d.ts.map +1 -0
- package/build/dts/server/watch.d.ts +10 -0
- package/build/dts/server/watch.d.ts.map +1 -0
- package/build/esm/index.js +14 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/main.js +134 -0
- package/build/esm/main.js.map +1 -0
- package/build/esm/model/atlas.js +431 -0
- package/build/esm/model/atlas.js.map +1 -0
- package/build/esm/model/campaigns.js +181 -0
- package/build/esm/model/campaigns.js.map +1 -0
- package/build/esm/server/collect.js +83 -0
- package/build/esm/server/collect.js.map +1 -0
- package/build/esm/server/compose.js +50 -0
- package/build/esm/server/compose.js.map +1 -0
- package/build/esm/server/export.js +19 -0
- package/build/esm/server/export.js.map +1 -0
- package/build/esm/server/handler.js +67 -0
- package/build/esm/server/handler.js.map +1 -0
- package/build/esm/server/serve.js +80 -0
- package/build/esm/server/serve.js.map +1 -0
- package/build/esm/server/site.js +6 -0
- package/build/esm/server/site.js.map +1 -0
- package/build/esm/server/source.js +67 -0
- package/build/esm/server/source.js.map +1 -0
- package/build/esm/server/vite-plugin.js +21 -0
- package/build/esm/server/vite-plugin.js.map +1 -0
- package/build/esm/server/watch.js +56 -0
- package/build/esm/server/watch.js.map +1 -0
- package/build/site/_astro/Browser.yxldcnQ3.js +3 -0
- package/build/site/_astro/client.Buyw3Q1S.js +9 -0
- package/build/site/_astro/index.DQrWs1cb.css +1 -0
- package/build/site/_astro/react.DJY1zw8Z.js +1 -0
- package/build/site/favicon.svg +1 -0
- package/build/site/index.html +8 -0
- package/package.json +78 -0
- package/src/app/components/Browser.tsx +153 -0
- package/src/app/components/architecture/ArchitectureBrowser.tsx +240 -0
- package/src/app/components/architecture/Detail.tsx +655 -0
- package/src/app/components/architecture/ManifestPane.tsx +178 -0
- package/src/app/components/architecture/Tree.tsx +205 -0
- package/src/app/components/architecture/rows.ts +181 -0
- package/src/app/components/architecture/selection.ts +6 -0
- package/src/app/components/campaigns/CampaignBrowser.tsx +146 -0
- package/src/app/components/campaigns/Detail.tsx +653 -0
- package/src/app/components/campaigns/Ladder.tsx +326 -0
- package/src/app/components/campaigns/pick.ts +7 -0
- package/src/app/components/lib/data.ts +132 -0
- package/src/app/pages/index.astro +34 -0
- package/src/app/public/favicon.svg +1 -0
- package/src/app/styles/theme.css +993 -0
- package/src/index.ts +37 -0
- package/src/main.ts +155 -0
- package/src/model/atlas.ts +751 -0
- package/src/model/campaigns.ts +376 -0
- package/src/server/collect.ts +132 -0
- package/src/server/compose.ts +79 -0
- package/src/server/export.ts +24 -0
- package/src/server/handler.ts +77 -0
- package/src/server/serve.ts +100 -0
- package/src/server/site.ts +6 -0
- package/src/server/source.ts +89 -0
- package/src/server/vite-plugin.ts +32 -0
- package/src/server/watch.ts +65 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import type { CampaignCard, CampaignView } from "../../../model/campaigns.js";
|
|
4
|
+
import { Detail } from "./Detail.js";
|
|
5
|
+
import { Ladder } from "./Ladder.js";
|
|
6
|
+
import type { Pick } from "./pick.js";
|
|
7
|
+
|
|
8
|
+
// The Campaign Browser: the ladder the docs draw, for real. A campaign's
|
|
9
|
+
// phases in order, each recognized by its objectives; its sectors standing at
|
|
10
|
+
// the first phase with residue for them; the end state last. Hover a phase,
|
|
11
|
+
// an objective or a sector and the rest of the ladder says how they relate;
|
|
12
|
+
// the pane beside it says what `campaigns` and `check` would.
|
|
13
|
+
|
|
14
|
+
// The selection in the hash: `<campaign>` alone, or `<campaign>/<kind>:<id>`.
|
|
15
|
+
const parseSelection = (
|
|
16
|
+
view: CampaignView,
|
|
17
|
+
text: string | null,
|
|
18
|
+
): { campaign: string | null; pick: Pick | null } => {
|
|
19
|
+
if (text === null) return { campaign: null, pick: null };
|
|
20
|
+
const at = text.indexOf("/");
|
|
21
|
+
const campaign = at === -1 ? text : text.slice(0, at);
|
|
22
|
+
if (!view.campaigns.some((one) => one.id === campaign)) return { campaign: null, pick: null };
|
|
23
|
+
if (at === -1) return { campaign, pick: null };
|
|
24
|
+
const rest = text.slice(at + 1);
|
|
25
|
+
const colon = rest.indexOf(":");
|
|
26
|
+
const kind = colon === -1 ? rest : rest.slice(0, colon);
|
|
27
|
+
const id = colon === -1 ? "" : rest.slice(colon + 1);
|
|
28
|
+
const pick: Pick | null =
|
|
29
|
+
kind === "campaign"
|
|
30
|
+
? { kind: "campaign" }
|
|
31
|
+
: kind === "phase"
|
|
32
|
+
? { kind: "phase", id }
|
|
33
|
+
: kind === "objective"
|
|
34
|
+
? { kind: "objective", id }
|
|
35
|
+
: kind === "sector"
|
|
36
|
+
? { kind: "sector", name: id }
|
|
37
|
+
: null;
|
|
38
|
+
return { campaign, pick };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const selectionText = (campaign: string, pick: Pick | null): string =>
|
|
42
|
+
pick === null
|
|
43
|
+
? campaign
|
|
44
|
+
: `${campaign}/${pick.kind}${pick.kind === "campaign" ? "" : `:${pick.kind === "sector" ? pick.name : pick.id}`}`;
|
|
45
|
+
|
|
46
|
+
type Props = {
|
|
47
|
+
readonly view: CampaignView;
|
|
48
|
+
readonly selection: string | null;
|
|
49
|
+
readonly onSelect: (selection: string | null) => void;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const CampaignBrowser = ({ onSelect, selection, view }: Props): React.JSX.Element => {
|
|
53
|
+
const parsed = useMemo(() => parseSelection(view, selection), [view, selection]);
|
|
54
|
+
const campaign: CampaignCard | undefined =
|
|
55
|
+
view.campaigns.find((one) => one.id === parsed.campaign) ?? view.campaigns[0];
|
|
56
|
+
const [hover, setHover] = useState<Pick | null>(null);
|
|
57
|
+
|
|
58
|
+
if (campaign === undefined) {
|
|
59
|
+
return <div className="placeholder">This policy declares no campaigns.</div>;
|
|
60
|
+
}
|
|
61
|
+
const selected = parsed.campaign === campaign.id ? parsed.pick : null;
|
|
62
|
+
const pick = (next: Pick | null): void => {
|
|
63
|
+
onSelect(selectionText(campaign.id, next));
|
|
64
|
+
};
|
|
65
|
+
const nudged = view.nudge?.sectors.filter((one) => one.campaign === campaign.id) ?? [];
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<div className="camp">
|
|
69
|
+
<section className="pane ladder-pane" aria-label="Campaign ladder">
|
|
70
|
+
<div className="pane-head tabs-row">
|
|
71
|
+
{view.campaigns.map((one) => (
|
|
72
|
+
<button
|
|
73
|
+
key={one.id}
|
|
74
|
+
type="button"
|
|
75
|
+
className={one.id === campaign.id ? "tab active" : "tab"}
|
|
76
|
+
onClick={() => {
|
|
77
|
+
onSelect(selectionText(one.id, null));
|
|
78
|
+
}}
|
|
79
|
+
title={one.title ?? one.id}
|
|
80
|
+
>
|
|
81
|
+
<span className="mono">{one.id}</span>
|
|
82
|
+
<span className={`pct ${one.complete ? "good" : one.stalled ? "bad" : ""}`}>
|
|
83
|
+
{Math.round(one.progress * 100)}%
|
|
84
|
+
</span>
|
|
85
|
+
</button>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
<div className="camp-head">
|
|
89
|
+
<h2>
|
|
90
|
+
{campaign.title ?? campaign.id}
|
|
91
|
+
{campaign.stalled ? <span className="flag bad">stalled</span> : null}
|
|
92
|
+
{campaign.complete ? <span className="flag good">complete</span> : null}
|
|
93
|
+
{campaign.missingLedger ? <span className="flag warn">no ledger</span> : null}
|
|
94
|
+
{!campaign.arithmetic ? <span className="flag bad">ledger arithmetic</span> : null}
|
|
95
|
+
</h2>
|
|
96
|
+
<div
|
|
97
|
+
className="progress"
|
|
98
|
+
title={`${String(Math.round(campaign.progress * 100))}% of the initial holdouts cleared`}
|
|
99
|
+
>
|
|
100
|
+
<div
|
|
101
|
+
className="progress-bar"
|
|
102
|
+
style={{ width: `${String(Math.round(campaign.progress * 100))}%` }}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="muted small">
|
|
106
|
+
{campaign.count} holdout{campaign.count === 1 ? "" : "s"} left
|
|
107
|
+
{campaign.owner === null ? "" : ` · ${campaign.owner}`}
|
|
108
|
+
{campaign.staleAfterDays === null
|
|
109
|
+
? ""
|
|
110
|
+
: ` · stale after ${String(campaign.staleAfterDays)}d`}
|
|
111
|
+
{" · scope "}
|
|
112
|
+
<span className="mono">{campaign.scope.join(", ")}</span>
|
|
113
|
+
{campaign.perimeter === null ? "" : ` · perimeter ${campaign.perimeter}`}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="ladder-scroll">
|
|
117
|
+
<Ladder
|
|
118
|
+
campaign={campaign}
|
|
119
|
+
nudged={new Set(nudged.map((one) => one.sector))}
|
|
120
|
+
hover={hover}
|
|
121
|
+
selected={selected}
|
|
122
|
+
onHover={setHover}
|
|
123
|
+
onPick={pick}
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
<div className="legend">
|
|
127
|
+
<span className="key defined">defined phase</span>
|
|
128
|
+
<span className="key open">open phase</span>
|
|
129
|
+
<span className="key sector">sector</span>
|
|
130
|
+
<span className="key legacy">legacy</span>
|
|
131
|
+
<span className="key done">done</span>
|
|
132
|
+
<span className="key nudged">touched in the working tree</span>
|
|
133
|
+
</div>
|
|
134
|
+
</section>
|
|
135
|
+
<section className="pane detail-pane" aria-label="Details">
|
|
136
|
+
<Detail
|
|
137
|
+
view={view}
|
|
138
|
+
campaign={campaign}
|
|
139
|
+
shown={hover ?? selected ?? { kind: "campaign" }}
|
|
140
|
+
pinned={hover === null && selected !== null}
|
|
141
|
+
onPick={pick}
|
|
142
|
+
/>
|
|
143
|
+
</section>
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
};
|