@mdbase-dev/ui 0.1.0-beta.126 → 0.1.0-beta.148
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 +168 -0
- package/css/collections.css +978 -0
- package/css/controls.css +33 -1
- package/css/screens.css +44 -2
- package/dist/AppSwitcher.js +187 -33
- package/dist/Checkbox.d.ts +14 -0
- package/dist/Checkbox.js +32 -0
- package/dist/CollectionModeControl.d.ts +11 -0
- package/dist/CollectionModeControl.js +290 -0
- package/dist/CommandPalette.d.ts +4 -2
- package/dist/CommandPalette.js +275 -103
- package/dist/Dialog.js +72 -25
- package/dist/Feedback.js +763 -229
- package/dist/FeedbackMarkup.d.ts +1 -1
- package/dist/FeedbackMarkup.js +326 -125
- package/dist/FeedbackVerification.js +115 -68
- package/dist/SaveNotice.js +71 -28
- package/dist/Select.js +183 -135
- package/dist/SelectList.js +59 -14
- package/dist/ThemeSelect.js +55 -4
- package/dist/apps.d.ts +1 -0
- package/dist/apps.js +50 -29
- package/dist/brand.js +251 -68
- package/dist/codemirror.js +116 -87
- package/dist/collections/CollectionGate.d.ts +52 -0
- package/dist/collections/CollectionGate.js +456 -0
- package/dist/collections/CollectionSaveWarning.d.ts +10 -0
- package/dist/collections/CollectionSaveWarning.js +28 -0
- package/dist/collections/CollectionSwitcher.d.ts +51 -0
- package/dist/collections/CollectionSwitcher.js +598 -0
- package/dist/collections/SetupReview.d.ts +9 -0
- package/dist/collections/SetupReview.js +216 -0
- package/dist/collections/copy.d.ts +100 -0
- package/dist/collections/copy.js +937 -0
- package/dist/collections/dialogs.d.ts +27 -0
- package/dist/collections/dialogs.js +169 -0
- package/dist/collections/model.d.ts +146 -0
- package/dist/collections/model.js +337 -0
- package/dist/collections/mount.d.ts +19 -0
- package/dist/collections/mount.js +56 -0
- package/dist/collections/parts.d.ts +86 -0
- package/dist/collections/parts.js +439 -0
- package/dist/collections/testing.d.ts +19 -0
- package/dist/collections/testing.js +39 -0
- package/dist/collections/use-collection-ui.d.ts +140 -0
- package/dist/collections/use-collection-ui.js +236 -0
- package/dist/collections.d.ts +19 -0
- package/dist/collections.js +13 -0
- package/dist/feedback-data.d.ts +13 -12
- package/dist/feedback-data.js +365 -168
- package/dist/flock.d.ts +63 -0
- package/dist/flock.js +140 -0
- package/dist/i18n.d.ts +6 -0
- package/dist/i18n.js +71 -0
- package/dist/locales/de/messages.json +1 -0
- package/dist/locales/en/messages.json +1 -0
- package/dist/locales/es/messages.json +1 -0
- package/dist/locales/fr/messages.json +1 -0
- package/dist/locales/it/messages.json +1 -0
- package/dist/locales/ja/messages.json +1 -0
- package/dist/locales/ko/messages.json +1 -0
- package/dist/locales/pt/messages.json +1 -0
- package/dist/locales/ru/messages.json +1 -0
- package/dist/locales/zh/messages.json +1 -0
- package/dist/mark-activity.js +82 -75
- package/dist/permission-selection.js +27 -17
- package/dist/popover.js +138 -130
- package/dist/screens.d.ts +20 -3
- package/dist/screens.js +275 -6
- package/dist/select-model.js +34 -28
- package/dist/text-diff.d.ts +1 -0
- package/dist/text-diff.js +106 -74
- package/dist/theme.js +34 -40
- package/package.json +56 -7
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { describeError } from "@mdbase-dev/sdk/describe";
|
|
3
|
+
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { actionNotice, problemControl } from "./model.js";
|
|
5
|
+
import { ActionButton, NoticeBlock } from "./parts.js";
|
|
6
|
+
/** Quiet re-plans after a transient refusal (about a minute in all). */
|
|
7
|
+
const TRANSIENT_REPLANS = 15;
|
|
8
|
+
/**
|
|
9
|
+
* Reviewable setup: `setup.plan()` (read-only, so it starts at once), then
|
|
10
|
+
* `setup.apply(plan)` only from the person's click. Never configures silently.
|
|
11
|
+
*/
|
|
12
|
+
export function SetupReview({
|
|
13
|
+
ui
|
|
14
|
+
}) {
|
|
15
|
+
const {
|
|
16
|
+
mdbase
|
|
17
|
+
} = ui.options;
|
|
18
|
+
const [state, setState] = useState({
|
|
19
|
+
status: "planning",
|
|
20
|
+
notice: null
|
|
21
|
+
});
|
|
22
|
+
const [request, setRequest] = useState({
|
|
23
|
+
options: {},
|
|
24
|
+
notice: null,
|
|
25
|
+
quiet: false
|
|
26
|
+
});
|
|
27
|
+
const [applying, setApplying] = useState(false);
|
|
28
|
+
const applyingNow = useRef(false);
|
|
29
|
+
const shown = useRef(state.status);
|
|
30
|
+
shown.current = state.status;
|
|
31
|
+
// Readiness re-checks whenever the collection changes (in another tab, or in
|
|
32
|
+
// the person's own editor). A review planned before that change can be stale:
|
|
33
|
+
// a restored type may now be upgradable, or a refusal resolved. Plan again,
|
|
34
|
+
// quietly, each time a re-check settles; an apply in flight owns the screen.
|
|
35
|
+
const checking = useSyncExternalStore(mdbase.subscribe, () => mdbase.getSnapshot().readiness.checking);
|
|
36
|
+
const wasChecking = useRef(checking);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const settled = wasChecking.current && !checking;
|
|
39
|
+
wasChecking.current = checking;
|
|
40
|
+
// Only a review on screen is kept: planning or a failure plans again plainly.
|
|
41
|
+
if (settled && !applyingNow.current) setRequest(current => ({
|
|
42
|
+
...current,
|
|
43
|
+
quiet: shown.current === "ready"
|
|
44
|
+
}));
|
|
45
|
+
}, [checking]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const controller = new AbortController();
|
|
48
|
+
let timer = null;
|
|
49
|
+
// A transient refusal (another tab is finishing setup, the SDK is busy)
|
|
50
|
+
// clears by itself: plan again quietly, with backoff, before giving up.
|
|
51
|
+
const attempt = n => {
|
|
52
|
+
mdbase.setup.plan({
|
|
53
|
+
...request.options,
|
|
54
|
+
signal: controller.signal
|
|
55
|
+
}).then(plan => {
|
|
56
|
+
if (controller.signal.aborted) return;
|
|
57
|
+
setState(current => ({
|
|
58
|
+
status: "ready",
|
|
59
|
+
plan,
|
|
60
|
+
notice: request.quiet ? current.notice : request.notice
|
|
61
|
+
}));
|
|
62
|
+
}, cause => {
|
|
63
|
+
if (controller.signal.aborted) return;
|
|
64
|
+
const described = describeError(cause);
|
|
65
|
+
if (request.quiet) return;
|
|
66
|
+
if (described?.transient && n < TRANSIENT_REPLANS) {
|
|
67
|
+
timer = setTimeout(() => attempt(n + 1), described.retryAfterMs ?? Math.min(500 * 2 ** n, 5000));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
setState({
|
|
71
|
+
status: "failed",
|
|
72
|
+
notice: actionNotice(described)
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
attempt(0);
|
|
77
|
+
return () => {
|
|
78
|
+
controller.abort();
|
|
79
|
+
if (timer !== null) clearTimeout(timer);
|
|
80
|
+
};
|
|
81
|
+
}, [mdbase, request]);
|
|
82
|
+
const replan = (options, notice = null) => {
|
|
83
|
+
setState({
|
|
84
|
+
status: "planning",
|
|
85
|
+
notice: null
|
|
86
|
+
});
|
|
87
|
+
setRequest({
|
|
88
|
+
options,
|
|
89
|
+
notice,
|
|
90
|
+
quiet: false
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
// Nothing here can change the collection: check it again, then plan again,
|
|
94
|
+
// after the person has fixed it elsewhere. Readiness may open it outright.
|
|
95
|
+
const recheck = () => {
|
|
96
|
+
void ui.actions.track("assess", () => mdbase.assess()).then(() => replan(request.options), () => {});
|
|
97
|
+
};
|
|
98
|
+
const apply = plan => {
|
|
99
|
+
setApplying(true);
|
|
100
|
+
applyingNow.current = true;
|
|
101
|
+
mdbase.setup.apply(plan).then(result => {
|
|
102
|
+
setApplying(false);
|
|
103
|
+
applyingNow.current = false;
|
|
104
|
+
if (result.outcome === "changed") replan(request.options, actionNotice(describeError("setup_changed")));
|
|
105
|
+
}, cause => {
|
|
106
|
+
setApplying(false);
|
|
107
|
+
applyingNow.current = false;
|
|
108
|
+
const notice = actionNotice(describeError(cause));
|
|
109
|
+
setState(current => current.status === "ready" ? {
|
|
110
|
+
...current,
|
|
111
|
+
notice
|
|
112
|
+
} : current);
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
if (state.status === "planning") {
|
|
116
|
+
return _jsxs("p", {
|
|
117
|
+
className: "mdbase-collections-progress",
|
|
118
|
+
role: "status",
|
|
119
|
+
children: [_jsx("span", {
|
|
120
|
+
className: "mdbase-collections-spinner",
|
|
121
|
+
"aria-hidden": "true"
|
|
122
|
+
}), ui.copy.setupChecking]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (state.status === "failed") {
|
|
126
|
+
return _jsxs("div", {
|
|
127
|
+
className: "mdbase-collections-setup",
|
|
128
|
+
children: [state.notice && _jsx(NoticeBlock, {
|
|
129
|
+
notice: state.notice,
|
|
130
|
+
ui: ui
|
|
131
|
+
}), _jsx("div", {
|
|
132
|
+
className: "mdbase-collections-actions",
|
|
133
|
+
children: _jsx(ActionButton, {
|
|
134
|
+
tone: "primary",
|
|
135
|
+
onClick: () => replan(request.options),
|
|
136
|
+
children: ui.copy.tryAgain
|
|
137
|
+
})
|
|
138
|
+
})]
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const {
|
|
142
|
+
plan
|
|
143
|
+
} = state;
|
|
144
|
+
const refusal = plan.refusal ? describeError(plan.refusal.code) : null;
|
|
145
|
+
const refusalNotice = refusal ? {
|
|
146
|
+
...actionNotice(refusal),
|
|
147
|
+
control: problemControl(refusal, {
|
|
148
|
+
collectionId: null,
|
|
149
|
+
signedIn: true,
|
|
150
|
+
open: true
|
|
151
|
+
})
|
|
152
|
+
} : null;
|
|
153
|
+
const nothing = plan.status === "current" || plan.changes.length === 0 && plan.conflicts.length === 0;
|
|
154
|
+
const adoptable = Object.keys(plan.adoptable).length > 0;
|
|
155
|
+
const applicable = !refusal && plan.canApply;
|
|
156
|
+
// NoticeBlock shows a refusal's own control, except `set_up` (this is setup).
|
|
157
|
+
const refusalActs = !!refusalNotice?.control && refusalNotice.control.kind !== "set_up";
|
|
158
|
+
// Never leave the review without a way forward.
|
|
159
|
+
const stuck = !applicable && !adoptable && !refusalActs;
|
|
160
|
+
return _jsxs("div", {
|
|
161
|
+
className: "mdbase-collections-setup",
|
|
162
|
+
children: [state.notice && _jsx(NoticeBlock, {
|
|
163
|
+
notice: state.notice,
|
|
164
|
+
ui: ui,
|
|
165
|
+
variant: "inline"
|
|
166
|
+
}), refusalNotice ? _jsx(NoticeBlock, {
|
|
167
|
+
notice: refusalNotice,
|
|
168
|
+
ui: ui
|
|
169
|
+
}) : _jsx("p", {
|
|
170
|
+
className: "mdbase-collections-setup-summary",
|
|
171
|
+
children: nothing ? ui.copy.setupNothing : plan.summary
|
|
172
|
+
}), plan.changes.length > 0 && _jsxs("section", {
|
|
173
|
+
className: "mdbase-collections-setup-section",
|
|
174
|
+
children: [_jsx("h3", {
|
|
175
|
+
children: ui.copy.setupChanges
|
|
176
|
+
}), _jsx("ul", {
|
|
177
|
+
className: "mdbase-collections-setup-list",
|
|
178
|
+
children: plan.changes.map(change => _jsx("li", {
|
|
179
|
+
children: change.message
|
|
180
|
+
}, `${change.kind}:${change.target}`))
|
|
181
|
+
})]
|
|
182
|
+
}), plan.conflicts.length > 0 && _jsxs("section", {
|
|
183
|
+
className: "mdbase-collections-setup-section",
|
|
184
|
+
children: [_jsx("h3", {
|
|
185
|
+
children: ui.copy.setupConflicts
|
|
186
|
+
}), _jsx("ul", {
|
|
187
|
+
className: "mdbase-collections-setup-list",
|
|
188
|
+
children: plan.conflicts.map(conflict => _jsx("li", {
|
|
189
|
+
children: conflict.message
|
|
190
|
+
}, conflict.target))
|
|
191
|
+
})]
|
|
192
|
+
}), _jsxs("div", {
|
|
193
|
+
className: "mdbase-collections-actions",
|
|
194
|
+
children: [applicable && _jsx(ActionButton, {
|
|
195
|
+
tone: "primary",
|
|
196
|
+
progress: applying ? ui.copy.setupApplying : null,
|
|
197
|
+
onClick: () => apply(plan),
|
|
198
|
+
children: nothing ? ui.copy.setupContinue : ui.copy.setupApply
|
|
199
|
+
}), adoptable && _jsx(ActionButton, {
|
|
200
|
+
tone: "secondary",
|
|
201
|
+
inert: applying,
|
|
202
|
+
onClick: () => replan({
|
|
203
|
+
...request.options,
|
|
204
|
+
adopt: plan.adoptable
|
|
205
|
+
}),
|
|
206
|
+
children: ui.say("setupUseAppVersion")
|
|
207
|
+
}), stuck && _jsx(ActionButton, {
|
|
208
|
+
tone: "primary",
|
|
209
|
+
inert: ui.busy !== null,
|
|
210
|
+
progress: ui.busy === "assess" ? ui.copy.setupChecking : null,
|
|
211
|
+
onClick: recheck,
|
|
212
|
+
children: ui.copy.tryAgain
|
|
213
|
+
})]
|
|
214
|
+
})]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import "../i18n.js";
|
|
2
|
+
/**
|
|
3
|
+
* The shared screens' own words. Problem titles and messages are the SDK's
|
|
4
|
+
* (`describeError`); these are headings, labels and confirmations around them.
|
|
5
|
+
* `{app}` is the app's name and `{collection}` a collection's name.
|
|
6
|
+
*/
|
|
7
|
+
export declare const defaultCollectionCopy: {
|
|
8
|
+
readonly startingHeading: string;
|
|
9
|
+
readonly signedOutHeading: string;
|
|
10
|
+
readonly signedOutLead: string;
|
|
11
|
+
readonly signedOutNotice: string;
|
|
12
|
+
readonly signedOutIncomplete: string;
|
|
13
|
+
readonly signIn: string;
|
|
14
|
+
readonly authorizingHeading: string;
|
|
15
|
+
readonly authorizingSignIn: string;
|
|
16
|
+
readonly authorizingAdd: string;
|
|
17
|
+
readonly authorizingApprove: string;
|
|
18
|
+
readonly openWindowAgain: string;
|
|
19
|
+
readonly cancel: string;
|
|
20
|
+
readonly chooseHeading: string;
|
|
21
|
+
readonly yourCollections: string;
|
|
22
|
+
readonly emptyHeading: string;
|
|
23
|
+
readonly emptyLead: string;
|
|
24
|
+
readonly noCollectionAdded: string;
|
|
25
|
+
readonly accessNotGiven: string;
|
|
26
|
+
readonly addCollection: string;
|
|
27
|
+
readonly addPrivateCollection: string;
|
|
28
|
+
readonly addingCollection: string;
|
|
29
|
+
readonly manageAccess: string;
|
|
30
|
+
readonly signedInAs: string;
|
|
31
|
+
readonly signOut: string;
|
|
32
|
+
readonly signingOut: string;
|
|
33
|
+
readonly openingHeading: string;
|
|
34
|
+
readonly openingHeadingUnnamed: string;
|
|
35
|
+
readonly openingSlow: string;
|
|
36
|
+
readonly openingJoining: string;
|
|
37
|
+
readonly openingStore: string;
|
|
38
|
+
readonly openingLog: string;
|
|
39
|
+
readonly openingSyncing: string;
|
|
40
|
+
readonly openingSnapshot: string;
|
|
41
|
+
readonly openingReplaying: string;
|
|
42
|
+
readonly openingKey: string;
|
|
43
|
+
readonly openingRead: string;
|
|
44
|
+
readonly openingSetup: string;
|
|
45
|
+
readonly chooseAnother: string;
|
|
46
|
+
readonly approvalHeading: string;
|
|
47
|
+
readonly approvalHeadingUnnamed: string;
|
|
48
|
+
readonly approvalLead: string;
|
|
49
|
+
readonly approvalDeclined: string;
|
|
50
|
+
readonly reviewAccess: string;
|
|
51
|
+
readonly setupHeading: string;
|
|
52
|
+
readonly setupLead: string;
|
|
53
|
+
readonly setupChecking: string;
|
|
54
|
+
readonly setupNothing: string;
|
|
55
|
+
readonly setupApply: string;
|
|
56
|
+
readonly setupApplying: string;
|
|
57
|
+
readonly setupContinue: string;
|
|
58
|
+
readonly setupUseAppVersion: string;
|
|
59
|
+
readonly setupConflicts: string;
|
|
60
|
+
readonly setupChanges: string;
|
|
61
|
+
readonly tryAgain: string;
|
|
62
|
+
readonly problemHeading: string;
|
|
63
|
+
readonly problemHeadingUnnamed: string;
|
|
64
|
+
readonly unavailableHeading: string;
|
|
65
|
+
readonly closedHeading: string;
|
|
66
|
+
readonly closedLead: string;
|
|
67
|
+
readonly reload: string;
|
|
68
|
+
readonly thisCollection: string;
|
|
69
|
+
readonly roleOwner: string;
|
|
70
|
+
readonly roleEditor: string;
|
|
71
|
+
readonly roleViewer: string;
|
|
72
|
+
readonly rowOpening: string;
|
|
73
|
+
readonly openNow: string;
|
|
74
|
+
readonly idHint: string;
|
|
75
|
+
readonly privateCollection: string;
|
|
76
|
+
readonly privateCollectionHint: string;
|
|
77
|
+
readonly collectionLabel: string;
|
|
78
|
+
readonly collectionsLabel: string;
|
|
79
|
+
readonly switchCollection: string;
|
|
80
|
+
readonly switchCollectionNone: string;
|
|
81
|
+
readonly findCollection: string;
|
|
82
|
+
readonly noMatches: string;
|
|
83
|
+
readonly moreFor: string;
|
|
84
|
+
readonly removeFromList: string;
|
|
85
|
+
readonly deleteCollection: string;
|
|
86
|
+
readonly forgetTitle: string;
|
|
87
|
+
readonly forgetBody: string;
|
|
88
|
+
readonly forgetConfirm: string;
|
|
89
|
+
readonly deleteTitle: string;
|
|
90
|
+
readonly deleteBody: string;
|
|
91
|
+
readonly deleteTypeName: string;
|
|
92
|
+
readonly deleteConfirm: string;
|
|
93
|
+
readonly deleting: string;
|
|
94
|
+
readonly signOutAnyway: string;
|
|
95
|
+
readonly close: string;
|
|
96
|
+
};
|
|
97
|
+
export type CollectionCopyKey = keyof typeof defaultCollectionCopy;
|
|
98
|
+
export type CollectionCopy = Record<CollectionCopyKey, string>;
|
|
99
|
+
/** Replace `{name}` placeholders. Unknown placeholders stay as written. */
|
|
100
|
+
export declare function fill(template: string, values: Readonly<Record<string, string>>): string;
|