grist-widget-sdk 0.3.27 → 0.5.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/CHANGELOG.md +84 -0
- package/README.md +79 -208
- package/dist/app.cjs +1336 -4415
- package/dist/app.cjs.map +1 -1
- package/dist/app.d.cts +6 -103
- package/dist/app.d.ts +6 -103
- package/dist/app.js +5 -7
- package/dist/chunk-4ZGVKGDF.js +204 -0
- package/dist/chunk-4ZGVKGDF.js.map +1 -0
- package/dist/{chunk-6Y2IONGJ.js → chunk-LSALXIGM.js} +191 -20
- package/dist/chunk-LSALXIGM.js.map +1 -0
- package/dist/chunk-MAPAWVQC.js +1898 -0
- package/dist/chunk-MAPAWVQC.js.map +1 -0
- package/dist/{chunk-YQNMIL33.js → chunk-OXA2VITC.js} +532 -181
- package/dist/chunk-OXA2VITC.js.map +1 -0
- package/dist/define-widget-B1FIvI-h.d.cts +433 -0
- package/dist/define-widget-B9gEtJP0.d.ts +433 -0
- package/dist/emulator/testing.cjs +3341 -6512
- package/dist/emulator/testing.cjs.map +1 -1
- package/dist/emulator/testing.d.cts +29 -14
- package/dist/emulator/testing.d.ts +29 -14
- package/dist/emulator/testing.js +78 -72
- package/dist/emulator/testing.js.map +1 -1
- package/dist/emulator.cjs +63 -249
- package/dist/emulator.cjs.map +1 -1
- package/dist/emulator.d.cts +6 -8
- package/dist/emulator.d.ts +6 -8
- package/dist/emulator.js +5 -11
- package/dist/index.cjs +1442 -4862
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -260
- package/dist/index.d.ts +81 -260
- package/dist/index.js +12 -158
- package/dist/mount-grist-widget-CkB-3iG_.d.ts +137 -0
- package/dist/mount-grist-widget-Dtelqao_.d.cts +137 -0
- package/dist/{inline-DpcT8tV9.d.ts → templates-CGmRIQBV.d.cts} +89 -35
- package/dist/{inline-C7DkvuET.d.cts → templates-Cq94qOcf.d.ts} +89 -35
- package/dist/{types-D3JxUCfL.d.cts → types-DBStn0Dk.d.cts} +2 -26
- package/dist/{types-D3JxUCfL.d.ts → types-DBStn0Dk.d.ts} +2 -26
- package/package.json +8 -37
- package/dist/advanced.cjs +0 -2799
- package/dist/advanced.cjs.map +0 -1
- package/dist/advanced.d.cts +0 -822
- package/dist/advanced.d.ts +0 -822
- package/dist/advanced.js +0 -872
- package/dist/advanced.js.map +0 -1
- package/dist/chunk-22U7WAY3.js +0 -463
- package/dist/chunk-22U7WAY3.js.map +0 -1
- package/dist/chunk-64SVRWW7.js +0 -758
- package/dist/chunk-64SVRWW7.js.map +0 -1
- package/dist/chunk-6Y2IONGJ.js.map +0 -1
- package/dist/chunk-B4AKVSWV.js +0 -2362
- package/dist/chunk-B4AKVSWV.js.map +0 -1
- package/dist/chunk-ELGVOHFD.js +0 -32
- package/dist/chunk-ELGVOHFD.js.map +0 -1
- package/dist/chunk-GTTD5HPO.js +0 -36
- package/dist/chunk-GTTD5HPO.js.map +0 -1
- package/dist/chunk-NO6FYX33.js +0 -2084
- package/dist/chunk-NO6FYX33.js.map +0 -1
- package/dist/chunk-OPTOYD2Y.js +0 -277
- package/dist/chunk-OPTOYD2Y.js.map +0 -1
- package/dist/chunk-PM3B4WYV.js +0 -12
- package/dist/chunk-PM3B4WYV.js.map +0 -1
- package/dist/chunk-REV5AUXD.js +0 -25
- package/dist/chunk-REV5AUXD.js.map +0 -1
- package/dist/chunk-YQNMIL33.js.map +0 -1
- package/dist/create-grist-emulator-BCqp1BF9.d.ts +0 -81
- package/dist/create-grist-emulator-DBM8OSIR.d.cts +0 -81
- package/dist/emulator/devtools.cjs +0 -1404
- package/dist/emulator/devtools.cjs.map +0 -1
- package/dist/emulator/devtools.d.cts +0 -48
- package/dist/emulator/devtools.d.ts +0 -48
- package/dist/emulator/devtools.js +0 -506
- package/dist/emulator/devtools.js.map +0 -1
- package/dist/emulator/transports.cjs +0 -1132
- package/dist/emulator/transports.cjs.map +0 -1
- package/dist/emulator/transports.d.cts +0 -38
- package/dist/emulator/transports.d.ts +0 -38
- package/dist/emulator/transports.js +0 -12
- package/dist/emulator/transports.js.map +0 -1
- package/dist/templates-8k2LKoO-.d.cts +0 -24
- package/dist/templates-DIwi_mvI.d.ts +0 -24
- package/dist/use-grist-reads-DUMvJgV7.d.ts +0 -480
- package/dist/use-grist-reads-DpjVuI0H.d.cts +0 -480
- package/dist/use-grist-types-DdOLEJXN.d.cts +0 -499
- package/dist/use-grist-types-kwxoFeub.d.ts +0 -499
- package/dist/use-grist-widget-options-from-context-CGOlQ5Vc.d.cts +0 -43
- package/dist/use-grist-widget-options-from-context-CGOlQ5Vc.d.ts +0 -43
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GristBoundary,
|
|
3
3
|
GristWidgetProvider,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
deriveColumnMappingStatus,
|
|
5
|
+
deriveStatus,
|
|
6
|
+
isAccessInsufficientError,
|
|
7
|
+
isGristWidget,
|
|
8
|
+
parseGristWidgetLinkingInfo,
|
|
9
|
+
useNearestGristState,
|
|
10
|
+
useWidgetMetadata
|
|
11
|
+
} from "./chunk-MAPAWVQC.js";
|
|
7
12
|
|
|
8
13
|
// src/app/deploy-path.ts
|
|
9
14
|
var VERSION_SEGMENT = /^v(\d+\.\d+\.\d+(?:-[\w.]+)?)$/;
|
|
@@ -60,7 +65,7 @@ function useTheme() {
|
|
|
60
65
|
return value ?? { theme: "system", resolvedTheme: systemTheme(), setTheme: noop };
|
|
61
66
|
}
|
|
62
67
|
function EmbeddedThemeProvider({ children }) {
|
|
63
|
-
const
|
|
68
|
+
const gristTheme = useNearestGristState().state.theme;
|
|
64
69
|
const system = useSystemTheme(gristTheme === null);
|
|
65
70
|
const resolvedTheme = gristTheme ?? system;
|
|
66
71
|
useApplyTheme(resolvedTheme);
|
|
@@ -175,6 +180,9 @@ function noop() {
|
|
|
175
180
|
import { StrictMode, Suspense } from "react";
|
|
176
181
|
import { createRoot } from "react-dom/client";
|
|
177
182
|
|
|
183
|
+
// src/app/chrome/local-dev-page.tsx
|
|
184
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
185
|
+
|
|
178
186
|
// src/app/chrome/tokens.ts
|
|
179
187
|
var token = (name, fallback) => `var(--${name}, ${fallback})`;
|
|
180
188
|
var tokens = {
|
|
@@ -255,7 +263,20 @@ function widgetTitle() {
|
|
|
255
263
|
|
|
256
264
|
// src/app/chrome/local-dev-page.tsx
|
|
257
265
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
266
|
+
function useHasDemo() {
|
|
267
|
+
const [has, setHas] = useState3(null);
|
|
268
|
+
useEffect3(() => {
|
|
269
|
+
if (typeof fetch !== "function") return;
|
|
270
|
+
let live = true;
|
|
271
|
+
fetch("demo/demo.grist", { method: "HEAD" }).then((res) => live && setHas(res.ok)).catch(() => live && setHas(false));
|
|
272
|
+
return () => {
|
|
273
|
+
live = false;
|
|
274
|
+
};
|
|
275
|
+
}, []);
|
|
276
|
+
return has;
|
|
277
|
+
}
|
|
258
278
|
function LocalDevPage() {
|
|
279
|
+
const hasDemo = useHasDemo();
|
|
259
280
|
return /* @__PURE__ */ jsxs("main", { style: pageStyle, children: [
|
|
260
281
|
/* @__PURE__ */ jsxs("p", { style: eyebrowStyle, children: [
|
|
261
282
|
widgetTitle(),
|
|
@@ -263,6 +284,29 @@ function LocalDevPage() {
|
|
|
263
284
|
] }),
|
|
264
285
|
/* @__PURE__ */ jsx2("h1", { style: headingStyle, children: "Not embedded in Grist" }),
|
|
265
286
|
/* @__PURE__ */ jsx2("p", { style: leadStyle, children: "This is the local dev server: there's no Grist here to render the widget against." }),
|
|
287
|
+
/* @__PURE__ */ jsxs("section", { style: cardStyle, children: [
|
|
288
|
+
/* @__PURE__ */ jsx2("h2", { style: cardTitleStyle, children: "Open it in Grist, in this browser" }),
|
|
289
|
+
hasDemo === false ? /* @__PURE__ */ jsxs("p", { style: cardTextStyle, children: [
|
|
290
|
+
"Run ",
|
|
291
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: "pnpm demo --save" }),
|
|
292
|
+
" once (it needs Docker): it writes",
|
|
293
|
+
" ",
|
|
294
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: "public/demo/" }),
|
|
295
|
+
", a document for this widget. Then",
|
|
296
|
+
" ",
|
|
297
|
+
/* @__PURE__ */ jsx2("a", { href: "demo/index.html", children: "demo/index.html" }),
|
|
298
|
+
" opens it in real Grist, running in the browser, on this dev server's widget: edits reload in place."
|
|
299
|
+
] }) : /* @__PURE__ */ jsxs("p", { style: cardTextStyle, children: [
|
|
300
|
+
/* @__PURE__ */ jsx2("a", { href: "demo/index.html", children: "Open demo/index.html" }),
|
|
301
|
+
": real Grist running in the browser (no server, nothing saved), with this dev server's widget, so edits reload in place. Its document is",
|
|
302
|
+
" ",
|
|
303
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: "public/demo/demo.grist" }),
|
|
304
|
+
": after changing the declared columns, run",
|
|
305
|
+
" ",
|
|
306
|
+
/* @__PURE__ */ jsx2("code", { style: codeStyle, children: "pnpm demo --save" }),
|
|
307
|
+
" (Docker) to regenerate it."
|
|
308
|
+
] })
|
|
309
|
+
] }),
|
|
266
310
|
/* @__PURE__ */ jsxs("section", { style: cardStyle, children: [
|
|
267
311
|
/* @__PURE__ */ jsx2("h2", { style: cardTitleStyle, children: "Check it against real Grist" }),
|
|
268
312
|
/* @__PURE__ */ jsxs("p", { style: cardTextStyle, children: [
|
|
@@ -290,7 +334,7 @@ function LocalDevPage() {
|
|
|
290
334
|
}
|
|
291
335
|
|
|
292
336
|
// src/app/chrome/release-page.tsx
|
|
293
|
-
import { useState as
|
|
337
|
+
import { useState as useState4 } from "react";
|
|
294
338
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
295
339
|
function ReleasePage({ channel }) {
|
|
296
340
|
const label = deployChannelLabel(channel);
|
|
@@ -349,7 +393,7 @@ function ChannelLink({
|
|
|
349
393
|
}
|
|
350
394
|
function InstallUrl() {
|
|
351
395
|
const url = window.location.href;
|
|
352
|
-
const [copied, setCopied] =
|
|
396
|
+
const [copied, setCopied] = useState4(false);
|
|
353
397
|
function copy() {
|
|
354
398
|
void navigator.clipboard?.writeText(url).then(() => {
|
|
355
399
|
setCopied(true);
|
|
@@ -407,26 +451,122 @@ function formatDate(iso) {
|
|
|
407
451
|
return Number.isNaN(date.getTime()) ? iso : date.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
|
|
408
452
|
}
|
|
409
453
|
|
|
454
|
+
// src/sdk/helpers/grist-sdk-alerts.ts
|
|
455
|
+
var TITLE = {
|
|
456
|
+
"column-mapping": "Column mapping",
|
|
457
|
+
"mapping-pending": "Column mapping",
|
|
458
|
+
"section-not-linked": "Widget linking",
|
|
459
|
+
"source-not-wired": "Widget linking",
|
|
460
|
+
"action-error": "Action error",
|
|
461
|
+
"access-insufficient": "Access level",
|
|
462
|
+
"map-back-skip": "Write skipped",
|
|
463
|
+
"cell-issues": "Unexpected values"
|
|
464
|
+
};
|
|
465
|
+
var SEVERITY = {
|
|
466
|
+
"column-mapping": "warning",
|
|
467
|
+
"mapping-pending": "info",
|
|
468
|
+
"section-not-linked": "warning",
|
|
469
|
+
"source-not-wired": "warning",
|
|
470
|
+
"action-error": "error",
|
|
471
|
+
"access-insufficient": "error",
|
|
472
|
+
"map-back-skip": "warning",
|
|
473
|
+
"cell-issues": "warning"
|
|
474
|
+
};
|
|
475
|
+
var alert = (kind, message, ariaRole = "status") => ({
|
|
476
|
+
id: kind,
|
|
477
|
+
kind,
|
|
478
|
+
severity: SEVERITY[kind],
|
|
479
|
+
title: TITLE[kind],
|
|
480
|
+
ariaRole,
|
|
481
|
+
message
|
|
482
|
+
});
|
|
483
|
+
function isWidgetSectionNotLinked(interaction) {
|
|
484
|
+
const linking = parseGristWidgetLinkingInfo(interaction?.linking);
|
|
485
|
+
return Boolean(linking) && linking.asTarget == null;
|
|
486
|
+
}
|
|
487
|
+
function getGristSdkAlerts(state, widget, options = {}) {
|
|
488
|
+
if (deriveStatus(state) !== "ready") return [];
|
|
489
|
+
const out = [];
|
|
490
|
+
const mapping = deriveColumnMappingStatus(widget.columns, state.mappings);
|
|
491
|
+
if (mapping.pending) out.push(alert("mapping-pending", "Waiting for Grist to report column mappings."));
|
|
492
|
+
else if (!mapping.ok) {
|
|
493
|
+
const parts = ["Column mapping is incomplete."];
|
|
494
|
+
if (mapping.missing.length) parts.push(`Required columns: ${mapping.missing.join(", ")}.`);
|
|
495
|
+
if (mapping.emptyMultiples.length) parts.push(`Assign at least one sheet column for: ${mapping.emptyMultiples.join(", ")}.`);
|
|
496
|
+
out.push(alert("column-mapping", parts.join(" ")));
|
|
497
|
+
}
|
|
498
|
+
const failure = state.actionError ?? state.readError;
|
|
499
|
+
if (failure && isAccessInsufficientError(failure)) {
|
|
500
|
+
out.push(
|
|
501
|
+
alert(
|
|
502
|
+
"access-insufficient",
|
|
503
|
+
'This operation requires "Full document access". Set the Access level to "Full document access" in the Custom widget configuration panel.',
|
|
504
|
+
"alert"
|
|
505
|
+
)
|
|
506
|
+
);
|
|
507
|
+
} else if (state.actionError) {
|
|
508
|
+
out.push(alert("action-error", state.actionError, "alert"));
|
|
509
|
+
}
|
|
510
|
+
if (state.mapBackSkipped.length) {
|
|
511
|
+
const list = state.mapBackSkipped.join(", ");
|
|
512
|
+
out.push(alert("map-back-skip", `mapBack skipped multi-mapped column${state.mapBackSkipped.length === 1 ? "" : "s"}: ${list}.`));
|
|
513
|
+
}
|
|
514
|
+
const cellIssues = widget.cellIssues ?? [];
|
|
515
|
+
if (cellIssues.length) {
|
|
516
|
+
const first = cellIssues[0];
|
|
517
|
+
const where = first.column ? `${first.column} in row ${first.rowId}` : `row ${first.rowId}`;
|
|
518
|
+
const more = cellIssues.length > 1 ? ` (and ${cellIssues.length - 1} more)` : "";
|
|
519
|
+
out.push(alert("cell-issues", `${where}: ${first.message}${more}. Check the column's type, or the cell, in Grist.`));
|
|
520
|
+
}
|
|
521
|
+
const linking = parseGristWidgetLinkingInfo(state.interaction?.linking);
|
|
522
|
+
if (widget.allowSelectBy) {
|
|
523
|
+
if (linking && !linking.asSource) {
|
|
524
|
+
out.push(
|
|
525
|
+
alert(
|
|
526
|
+
"source-not-wired",
|
|
527
|
+
"This widget is configured as a selector (allowSelectBy) but no other section is using it as a link source yet. Link another section to read from this widget."
|
|
528
|
+
)
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
} else if (isWidgetSectionNotLinked(state.interaction)) {
|
|
532
|
+
const hint = options.sectionNotLinkedHint?.trim();
|
|
533
|
+
out.push(
|
|
534
|
+
alert(
|
|
535
|
+
"section-not-linked",
|
|
536
|
+
"This widget section is not linked to a table or selector. Link it from the page settings so the selected row is sent to the widget." + (hint ? ` ${hint}` : "")
|
|
537
|
+
)
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
const suppressed = widget.suppressAlerts ?? [];
|
|
541
|
+
return out.filter((a) => !suppressed.includes(a.kind));
|
|
542
|
+
}
|
|
543
|
+
function useGristSdkAlerts(options) {
|
|
544
|
+
const { connection, state, cellIssues } = useNearestGristState();
|
|
545
|
+
if (!connection) return [];
|
|
546
|
+
const { columns, allowSelectBy, suppressAlerts } = connection.runtime.options;
|
|
547
|
+
return getGristSdkAlerts(state, { columns, allowSelectBy, suppressAlerts, cellIssues }, options);
|
|
548
|
+
}
|
|
549
|
+
|
|
410
550
|
// src/app/chrome/widget-alerts.tsx
|
|
411
551
|
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
412
552
|
var ALERT_OPTIONS = {
|
|
413
553
|
sectionNotLinkedHint: "In Grist, select this widget section on the page, then use Link to connect a table, card, or chart view as the selector."
|
|
414
554
|
};
|
|
415
555
|
function WidgetAlerts({ children }) {
|
|
416
|
-
const alerts =
|
|
556
|
+
const alerts = useGristSdkAlerts(ALERT_OPTIONS);
|
|
417
557
|
if (alerts.length === 0) return /* @__PURE__ */ jsx4(Fragment, { children });
|
|
418
558
|
return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
|
|
419
|
-
/* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem", padding: "0.75rem 0.75rem 0" }, children: alerts.map((
|
|
559
|
+
/* @__PURE__ */ jsx4("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem", padding: "0.75rem 0.75rem 0" }, children: alerts.map((alert2) => /* @__PURE__ */ jsx4(WidgetAlert, { alert: alert2 }, alert2.id)) }),
|
|
420
560
|
children
|
|
421
561
|
] });
|
|
422
562
|
}
|
|
423
|
-
function WidgetAlert({ alert }) {
|
|
424
|
-
const linkIcon =
|
|
425
|
-
return /* @__PURE__ */ jsxs3("div", { role:
|
|
563
|
+
function WidgetAlert({ alert: alert2 }) {
|
|
564
|
+
const linkIcon = alert2.kind === "section-not-linked" || alert2.kind === "source-not-wired";
|
|
565
|
+
return /* @__PURE__ */ jsxs3("div", { role: alert2.ariaRole, style: { ...alertStyle, ...SEVERITY2[alert2.severity] }, children: [
|
|
426
566
|
/* @__PURE__ */ jsx4("span", { "aria-hidden": true, style: { flexShrink: 0, marginTop: "0.125rem" }, children: linkIcon ? /* @__PURE__ */ jsx4(LinkIcon, {}) : /* @__PURE__ */ jsx4(WarningIcon, {}) }),
|
|
427
567
|
/* @__PURE__ */ jsxs3("div", { style: { minWidth: 0 }, children: [
|
|
428
|
-
/* @__PURE__ */ jsx4("p", { style: { margin: 0, fontWeight: 500 }, children:
|
|
429
|
-
/* @__PURE__ */ jsx4("p", { style: { margin: "0.25rem 0 0", color: tokens.mutedForeground }, children:
|
|
568
|
+
/* @__PURE__ */ jsx4("p", { style: { margin: 0, fontWeight: 500 }, children: alert2.title }),
|
|
569
|
+
/* @__PURE__ */ jsx4("p", { style: { margin: "0.25rem 0 0", color: tokens.mutedForeground }, children: alert2.message })
|
|
430
570
|
] })
|
|
431
571
|
] });
|
|
432
572
|
}
|
|
@@ -441,7 +581,7 @@ var alertStyle = {
|
|
|
441
581
|
fontSize: "0.875rem",
|
|
442
582
|
lineHeight: 1.5
|
|
443
583
|
};
|
|
444
|
-
var
|
|
584
|
+
var SEVERITY2 = {
|
|
445
585
|
error: { borderColor: tokens.destructive, color: tokens.destructive },
|
|
446
586
|
warning: { borderColor: "rgb(245 158 11 / 0.4)", background: "rgb(245 158 11 / 0.1)" },
|
|
447
587
|
info: { background: "transparent" }
|
|
@@ -472,9 +612,28 @@ function LinkIcon() {
|
|
|
472
612
|
}
|
|
473
613
|
|
|
474
614
|
// src/app/mount-grist-widget.tsx
|
|
475
|
-
import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
|
|
476
|
-
function
|
|
477
|
-
const
|
|
615
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
616
|
+
function entryProps(entry, dev = false) {
|
|
617
|
+
const widget = isGristWidget(entry.widget) ? entry.widget : void 0;
|
|
618
|
+
const options = widget?.options ?? entry.GRIST_OPTIONS;
|
|
619
|
+
if (!options) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
"mountGristWidget: src/app/App.tsx exports neither `widget` (defineGristWidget) nor `GRIST_OPTIONS`."
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
const props = {
|
|
625
|
+
App: entry.default,
|
|
626
|
+
options,
|
|
627
|
+
metadata: widget?.metadata ?? entry.WIDGET_METADATA,
|
|
628
|
+
dev
|
|
629
|
+
};
|
|
630
|
+
if ("Alerts" in entry) props.Alerts = entry.Alerts;
|
|
631
|
+
if (entry.LocalDevPage) props.LocalDevPage = entry.LocalDevPage;
|
|
632
|
+
if (entry.ReleasePage) props.ReleasePage = entry.ReleasePage;
|
|
633
|
+
return props;
|
|
634
|
+
}
|
|
635
|
+
function mountGristWidget(config, entryOptions = {}) {
|
|
636
|
+
const { root = "#root", ...props } = "App" in config ? config : { ...entryProps(config, entryOptions.dev), root: entryOptions.root };
|
|
478
637
|
const container = typeof root === "string" ? document.querySelector(root) : root;
|
|
479
638
|
if (!container) {
|
|
480
639
|
throw new Error(`mountGristWidget: no element matches "${String(root)}" to render the widget into.`);
|
|
@@ -488,6 +647,7 @@ function mountGristWidget(config) {
|
|
|
488
647
|
function GristWidgetApp({
|
|
489
648
|
App,
|
|
490
649
|
options,
|
|
650
|
+
metadata,
|
|
491
651
|
dev = false,
|
|
492
652
|
Alerts = WidgetAlerts,
|
|
493
653
|
LocalDevPage: LocalDevPage2 = LocalDevPage,
|
|
@@ -495,15 +655,25 @@ function GristWidgetApp({
|
|
|
495
655
|
}) {
|
|
496
656
|
const Wrap = Alerts ?? PassThrough;
|
|
497
657
|
if (isEmbedded()) {
|
|
498
|
-
return /* @__PURE__ */
|
|
658
|
+
return /* @__PURE__ */ jsxs4(GristWidgetProvider, { options, children: [
|
|
659
|
+
/* @__PURE__ */ jsx5(PageMetadata, { metadata }),
|
|
660
|
+
/* @__PURE__ */ jsx5(EmbeddedThemeProvider, { children: /* @__PURE__ */ jsx5(GristBoundary, { children: /* @__PURE__ */ jsx5("div", { style: FILL, children: /* @__PURE__ */ jsx5(Wrap, { children: /* @__PURE__ */ jsx5(Suspense, { fallback: null, children: /* @__PURE__ */ jsx5(App, {}) }) }) }) }) })
|
|
661
|
+
] });
|
|
499
662
|
}
|
|
500
663
|
const channel = parseDeployPath(window.location.pathname).channel;
|
|
501
|
-
return /* @__PURE__ */
|
|
664
|
+
return /* @__PURE__ */ jsxs4(StandaloneThemeProvider, { children: [
|
|
665
|
+
/* @__PURE__ */ jsx5(PageMetadata, { metadata }),
|
|
666
|
+
/* @__PURE__ */ jsx5("div", { style: FILL, children: dev ? /* @__PURE__ */ jsx5(LocalDevPage2, {}) : /* @__PURE__ */ jsx5(ReleasePage2, { channel }) })
|
|
667
|
+
] });
|
|
502
668
|
}
|
|
503
669
|
var FILL = { minHeight: "100%", width: "100%" };
|
|
504
670
|
function isEmbedded() {
|
|
505
671
|
return typeof window !== "undefined" && window.self !== window.top;
|
|
506
672
|
}
|
|
673
|
+
function PageMetadata({ metadata }) {
|
|
674
|
+
useWidgetMetadata(metadata?.title ? { title: metadata.title, description: metadata.description ?? "" } : null);
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
507
677
|
function PassThrough({ children }) {
|
|
508
678
|
return /* @__PURE__ */ jsx5(Fragment2, { children });
|
|
509
679
|
}
|
|
@@ -513,7 +683,8 @@ export {
|
|
|
513
683
|
deployChannelLabel,
|
|
514
684
|
useReleasedVersions,
|
|
515
685
|
useTheme,
|
|
686
|
+
entryProps,
|
|
516
687
|
mountGristWidget,
|
|
517
688
|
GristWidgetApp
|
|
518
689
|
};
|
|
519
|
-
//# sourceMappingURL=chunk-
|
|
690
|
+
//# sourceMappingURL=chunk-LSALXIGM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/app/deploy-path.ts","../src/app/released-versions.ts","../src/app/widget-theme.tsx","../src/app/mount-grist-widget.tsx","../src/app/chrome/local-dev-page.tsx","../src/app/chrome/tokens.ts","../src/app/chrome/widget-title.ts","../src/app/chrome/release-page.tsx","../src/sdk/helpers/grist-sdk-alerts.ts","../src/app/chrome/widget-alerts.tsx"],"sourcesContent":["/**\n * Where a deployed build is being served from.\n *\n * A widget's deploy pipeline publishes the same build at its root and at\n * `/latest/`, a preview at `/dev/`, and each release at `/v<version>/`, all\n * under one base path (e.g. `/my-widget/` on GitHub Pages). Opened outside\n * Grist, the page tells them apart from `window.location.pathname` alone.\n */\nexport type DeployChannel =\n | { kind: \"latest\" }\n | { kind: \"dev\" }\n | { kind: \"version\"; version: string }\n\nexport type DeployPath = {\n channel: DeployChannel\n /**\n * The path the channels live under, always ending in `/`: the current\n * path without its channel segment. `\"/my-widget/v1.2.0/\"` gives\n * `\"/my-widget/\"`.\n */\n basePath: string\n}\n\nconst VERSION_SEGMENT = /^v(\\d+\\.\\d+\\.\\d+(?:-[\\w.]+)?)$/\n\n/**\n * Parse a pathname into its deploy channel and base path. A path without a\n * channel segment (the deployed root, or a local dev server) is `latest`:\n * the deploy places the latest release at the root too.\n */\nexport function parseDeployPath(pathname: string): DeployPath {\n const segments = pathname.replace(/\\/+$/, \"\").split(\"/\")\n const last = segments[segments.length - 1] ?? \"\"\n\n let channel: DeployChannel | null = null\n if (last === \"latest\") channel = { kind: \"latest\" }\n else if (last === \"dev\") channel = { kind: \"dev\" }\n else {\n const match = VERSION_SEGMENT.exec(last)\n if (match) channel = { kind: \"version\", version: match[1] }\n }\n\n if (!channel) return { channel: { kind: \"latest\" }, basePath: withTrailingSlash(pathname) }\n return { channel, basePath: withTrailingSlash(segments.slice(0, -1).join(\"/\")) }\n}\n\n/** `\"latest\"`, `\"dev\"` or `\"v1.2.0\"`. */\nexport function deployChannelLabel(channel: DeployChannel): string {\n return channel.kind === \"version\" ? `v${channel.version}` : channel.kind\n}\n\nfunction withTrailingSlash(path: string): string {\n return path.endsWith(\"/\") ? path : `${path}/`\n}\n","import { useEffect, useState } from \"react\"\n\nimport { parseDeployPath } from \"./deploy-path\"\n\n/** One entry of the `versions.json` a widget's deploy pipeline maintains. */\nexport type ReleasedVersion = {\n version: string\n publishedAt: string\n}\n\nexport type UseReleasedVersionsResult = {\n /** Newest first, as the deploy writes them; `null` until loaded. */\n versions: ReleasedVersion[] | null\n /** `true` when `versions.json` couldn't be fetched or read. */\n error: boolean\n}\n\n/**\n * The widget's released versions, read from the `versions.json` next to its\n * channels (see {@link parseDeployPath}). For a page shown outside Grist,\n * such as a release page linking to each version.\n */\nexport function useReleasedVersions(\n pathname: string = typeof window === \"undefined\" ? \"/\" : window.location.pathname\n): UseReleasedVersionsResult {\n const url = `${parseDeployPath(pathname).basePath}versions.json`\n const [result, setResult] = useState<UseReleasedVersionsResult>({ versions: null, error: false })\n\n useEffect(() => {\n let cancelled = false\n fetch(url, { cache: \"no-store\" })\n .then((res) => (res.ok ? res.json() : Promise.reject(new Error(`HTTP ${res.status}`))))\n .then((data: unknown) => {\n if (cancelled) return\n if (!Array.isArray(data)) throw new Error(\"versions.json is not an array\")\n setResult({ versions: data as ReleasedVersion[], error: false })\n })\n .catch(() => {\n if (!cancelled) setResult({ versions: null, error: true })\n })\n return () => {\n cancelled = true\n }\n }, [url])\n\n return result\n}\n","import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from \"react\"\n\nimport { useNearestGristState } from \"../sdk/use-grist\"\n\nexport type WidgetTheme = \"light\" | \"dark\" | \"system\"\nexport type ResolvedWidgetTheme = \"light\" | \"dark\"\n\nexport type UseThemeResult = {\n /**\n * Inside Grist: Grist's theme, or `\"system\"` until Grist has sent it.\n * Outside Grist: the saved choice, `\"system\"` by default.\n */\n theme: WidgetTheme\n /** The theme applied to `<html class>`: `\"system\"` resolved to light or dark. */\n resolvedTheme: ResolvedWidgetTheme\n /**\n * Choose a theme outside Grist; it is saved in `localStorage`. Inside Grist\n * this does nothing: the widget follows Grist's theme.\n */\n setTheme: (theme: WidgetTheme) => void\n}\n\nconst STORAGE_KEY = \"theme\"\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\"\nconst THEMES: readonly WidgetTheme[] = [\"light\", \"dark\", \"system\"]\n\nconst ThemeContext = createContext<UseThemeResult | null>(null)\n\n/**\n * The widget's current theme, for components that need it in JavaScript\n * (a toaster, a chart). Same shape as shadcn's `useTheme()`. Styles should\n * rely on the `dark` class that {@link mountGristWidget} keeps on `<html>`.\n * Outside a mounted widget (a unit test, for instance) it reports the\n * system theme and `setTheme` does nothing.\n */\nexport function useTheme(): UseThemeResult {\n const value = useContext(ThemeContext)\n return value ?? { theme: \"system\", resolvedTheme: systemTheme(), setTheme: noop }\n}\n\n/** Inside Grist: follows Grist's theme, and the OS setting until Grist sends one. */\nexport function EmbeddedThemeProvider({ children }: { children: ReactNode }) {\n const gristTheme = useNearestGristState().state.theme\n const system = useSystemTheme(gristTheme === null)\n const resolvedTheme = gristTheme ?? system\n\n useApplyTheme(resolvedTheme)\n\n const value = useMemo<UseThemeResult>(\n () => ({ theme: gristTheme ?? \"system\", resolvedTheme, setTheme: noop }),\n [gristTheme, resolvedTheme]\n )\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>\n}\n\n/**\n * Outside Grist (`pnpm dev`, the deployed release page): the saved choice,\n * the OS setting by default, and the `d` key to switch between light and\n * dark, as shadcn's theme provider does.\n */\nexport function StandaloneThemeProvider({ children }: { children: ReactNode }) {\n const [theme, setThemeState] = useState<WidgetTheme>(() => readStoredTheme() ?? \"system\")\n const system = useSystemTheme(theme === \"system\")\n const resolvedTheme = theme === \"system\" ? system : theme\n\n useApplyTheme(resolvedTheme)\n\n const setTheme = useCallback((next: WidgetTheme) => {\n writeStoredTheme(next)\n setThemeState(next)\n }, [])\n\n // `d` switches between light and dark, unless typing or using a shortcut.\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.repeat || event.metaKey || event.ctrlKey || event.altKey) return\n if (event.key.toLowerCase() !== \"d\" || isEditableTarget(event.target)) return\n setThemeState((current) => {\n const currentResolved = current === \"system\" ? systemTheme() : current\n const next: WidgetTheme = currentResolved === \"dark\" ? \"light\" : \"dark\"\n writeStoredTheme(next)\n return next\n })\n }\n window.addEventListener(\"keydown\", onKeyDown)\n return () => window.removeEventListener(\"keydown\", onKeyDown)\n }, [])\n\n // Another tab saving a choice applies here too.\n useEffect(() => {\n const onStorage = (event: StorageEvent) => {\n if (event.key !== STORAGE_KEY) return\n setThemeState(isTheme(event.newValue) ? event.newValue : \"system\")\n }\n window.addEventListener(\"storage\", onStorage)\n return () => window.removeEventListener(\"storage\", onStorage)\n }, [])\n\n const value = useMemo<UseThemeResult>(\n () => ({ theme, resolvedTheme, setTheme }),\n [theme, resolvedTheme, setTheme]\n )\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>\n}\n\n/** The OS setting, kept current while `active`. */\nfunction useSystemTheme(active: boolean): ResolvedWidgetTheme {\n const [theme, setTheme] = useState<ResolvedWidgetTheme>(systemTheme)\n\n useEffect(() => {\n if (!active || typeof window.matchMedia !== \"function\") return\n const query = window.matchMedia(DARK_QUERY)\n const onChange = () => setTheme(query.matches ? \"dark\" : \"light\")\n onChange()\n query.addEventListener(\"change\", onChange)\n return () => query.removeEventListener(\"change\", onChange)\n }, [active])\n\n return theme\n}\n\n/** Keep `light` or `dark` on `<html class>`, which Tailwind's dark variant reads. */\nfunction useApplyTheme(theme: ResolvedWidgetTheme) {\n useEffect(() => {\n const root = document.documentElement\n if (root.classList.contains(theme) && !root.classList.contains(other(theme))) return\n const restoreTransitions = suspendTransitions()\n root.classList.remove(other(theme))\n root.classList.add(theme)\n restoreTransitions()\n }, [theme])\n}\n\nfunction systemTheme(): ResolvedWidgetTheme {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return \"light\"\n return window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\"\n}\n\nfunction other(theme: ResolvedWidgetTheme): ResolvedWidgetTheme {\n return theme === \"dark\" ? \"light\" : \"dark\"\n}\n\n/** Switch themes without every color transition animating at once. */\nfunction suspendTransitions(): () => void {\n const style = document.createElement(\"style\")\n style.appendChild(\n document.createTextNode(\"*,*::before,*::after{-webkit-transition:none!important;transition:none!important}\")\n )\n document.head.appendChild(style)\n return () => {\n window.getComputedStyle(document.body)\n requestAnimationFrame(() => requestAnimationFrame(() => style.remove()))\n }\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false\n return target.isContentEditable || target.closest(\"input, textarea, select, [contenteditable='true']\") !== null\n}\n\nfunction isTheme(value: string | null): value is WidgetTheme {\n return value !== null && (THEMES as readonly string[]).includes(value)\n}\n\nfunction readStoredTheme(): WidgetTheme | null {\n try {\n const value = localStorage.getItem(STORAGE_KEY)\n return isTheme(value) ? value : null\n } catch {\n return null\n }\n}\n\nfunction writeStoredTheme(theme: WidgetTheme) {\n try {\n localStorage.setItem(STORAGE_KEY, theme)\n } catch {\n // Storage blocked (private mode, sandboxed iframe): the choice lasts for this page only.\n }\n}\n\nfunction noop() {}\n","import { StrictMode, Suspense, type ComponentType, type ReactNode } from \"react\"\nimport { createRoot } from \"react-dom/client\"\n\nimport { GristBoundary, GristWidgetProvider, useWidgetMetadata } from \"../sdk\"\nimport type { GristWidget, UseGristOptions } from \"../sdk\"\nimport { isGristWidget } from \"../sdk/define-widget\"\nimport { LocalDevPage as DefaultLocalDevPage } from \"./chrome/local-dev-page\"\nimport { ReleasePage as DefaultReleasePage } from \"./chrome/release-page\"\nimport { WidgetAlerts } from \"./chrome/widget-alerts\"\nimport { parseDeployPath, type DeployChannel } from \"./deploy-path\"\nimport { EmbeddedThemeProvider, StandaloneThemeProvider } from \"./widget-theme\"\n\nexport type GristWidgetAppProps = {\n /** The widget. A `React.lazy()` component works too: its code then loads only inside Grist. */\n App: ComponentType\n /** The widget's `GRIST_OPTIONS`: access level, columns, and so on. */\n options: UseGristOptions\n /** The page's title and description (the widget's `WIDGET_METADATA`, or its declaration's). */\n metadata?: { title?: string; description?: string }\n /**\n * Whether this is the local dev server. Pass `import.meta.env.DEV`.\n * Outside Grist it picks the local-dev page over the release page.\n */\n dev?: boolean\n /**\n * Wraps the widget inside Grist, under {@link GristBoundary}. The default\n * lists what needs the user's attention (column mapping, access level,\n * failed writes) above the widget; `null` renders the widget alone.\n */\n Alerts?: ComponentType<{ children: ReactNode }> | null\n /** Shown outside Grist on the local dev server. Defaults to a page pointing at `pnpm test` and the `dev` loop. */\n LocalDevPage?: ComponentType\n /**\n * Shown outside Grist on a deployed URL (`/latest/`, `/dev/`, `/v<version>/`).\n * Defaults to a page linking every channel and version, with the URL to\n * paste into Grist.\n */\n ReleasePage?: ComponentType<{ channel: DeployChannel }>\n}\n\nexport type MountGristWidgetConfig = GristWidgetAppProps & {\n /** The element to render into, or its CSS selector. Defaults to `#root`. */\n root?: Element | string\n}\n\n/**\n * A widget's entry module, `src/app/App.tsx`, as `import * as entry` gives\n * it: the widget as its default export, its declaration as `widget` (or, in\n * a widget made before `defineGristWidget`, `GRIST_OPTIONS` and\n * `WIDGET_METADATA`), and optionally its own `Alerts`, `LocalDevPage` and\n * `ReleasePage`. Everything a widget customises lives there, in its own\n * file: the template's `src/main.tsx` stays the same in every widget, and\n * `create-grist-widget upgrade` can rewrite it without losing anything.\n */\nexport type GristWidgetEntry = {\n default: ComponentType\n widget?: GristWidget\n GRIST_OPTIONS?: UseGristOptions\n WIDGET_METADATA?: { title?: string; description?: string }\n Alerts?: GristWidgetAppProps[\"Alerts\"]\n LocalDevPage?: GristWidgetAppProps[\"LocalDevPage\"]\n ReleasePage?: GristWidgetAppProps[\"ReleasePage\"]\n}\n\n/** The props {@link GristWidgetApp} takes, from a widget's entry module. */\nexport function entryProps(entry: GristWidgetEntry, dev = false): GristWidgetAppProps {\n const widget = isGristWidget(entry.widget) ? entry.widget : undefined\n const options = widget?.options ?? entry.GRIST_OPTIONS\n if (!options) {\n throw new Error(\n \"mountGristWidget: src/app/App.tsx exports neither `widget` (defineGristWidget) nor `GRIST_OPTIONS`.\"\n )\n }\n const props: GristWidgetAppProps = {\n App: entry.default,\n options,\n metadata: widget?.metadata ?? entry.WIDGET_METADATA,\n dev,\n }\n if (\"Alerts\" in entry) props.Alerts = entry.Alerts\n if (entry.LocalDevPage) props.LocalDevPage = entry.LocalDevPage\n if (entry.ReleasePage) props.ReleasePage = entry.ReleasePage\n return props\n}\n\nexport type MountedGristWidget = {\n unmount: () => void\n}\n\n/**\n * Render a widget into the page: everything `src/main.tsx` used to wire by\n * hand.\n *\n * ```tsx\n * import { mountGristWidget } from \"grist-widget-sdk/app\"\n * import \"./index.css\"\n * import * as entry from \"./app/App\"\n *\n * mountGristWidget(entry, { dev: import.meta.env.DEV })\n * ```\n *\n * It reads the entry module's exports ({@link GristWidgetEntry}): the widget,\n * its declaration and any page it replaces. The older form,\n * `mountGristWidget({ App, options: GRIST_OPTIONS, dev })`, still works.\n *\n * Inside Grist, the widget renders in {@link GristWidgetProvider} and\n * {@link GristBoundary}, which gates on its mapped columns when it declares\n * any, and follows Grist's light/dark theme. Outside Grist, where there is\n * nothing to connect to, the page shows `LocalDevPage` on the dev server and\n * `ReleasePage` on a deployed URL.\n */\nexport function mountGristWidget(entry: GristWidgetEntry, options?: { dev?: boolean; root?: Element | string }): MountedGristWidget\nexport function mountGristWidget(config: MountGristWidgetConfig): MountedGristWidget\nexport function mountGristWidget(\n config: MountGristWidgetConfig | GristWidgetEntry,\n entryOptions: { dev?: boolean; root?: Element | string } = {}\n): MountedGristWidget {\n const { root = \"#root\", ...props } =\n \"App\" in config ? config : { ...entryProps(config, entryOptions.dev), root: entryOptions.root }\n const container = typeof root === \"string\" ? document.querySelector(root) : root\n if (!container) {\n throw new Error(`mountGristWidget: no element matches \"${String(root)}\" to render the widget into.`)\n }\n\n const reactRoot = createRoot(container)\n reactRoot.render(\n <StrictMode>\n <GristWidgetApp {...props} />\n </StrictMode>\n )\n return { unmount: () => reactRoot.unmount() }\n}\n\n/**\n * The tree {@link mountGristWidget} renders, as a component: for a test, or\n * for a page that manages its own React root.\n */\nexport function GristWidgetApp({\n App,\n options,\n metadata,\n dev = false,\n Alerts = WidgetAlerts,\n LocalDevPage = DefaultLocalDevPage,\n ReleasePage = DefaultReleasePage,\n}: GristWidgetAppProps) {\n const Wrap = Alerts ?? PassThrough\n if (isEmbedded()) {\n return (\n <GristWidgetProvider options={options}>\n <PageMetadata metadata={metadata} />\n <EmbeddedThemeProvider>\n <GristBoundary>\n <div style={FILL}>\n <Wrap>\n <Suspense fallback={null}>\n <App />\n </Suspense>\n </Wrap>\n </div>\n </GristBoundary>\n </EmbeddedThemeProvider>\n </GristWidgetProvider>\n )\n }\n\n const channel = parseDeployPath(window.location.pathname).channel\n return (\n <StandaloneThemeProvider>\n <PageMetadata metadata={metadata} />\n <div style={FILL}>{dev ? <LocalDevPage /> : <ReleasePage channel={channel} />}</div>\n </StandaloneThemeProvider>\n )\n}\n\nconst FILL = { minHeight: \"100%\", width: \"100%\" } as const\n\n/** A Grist custom widget only ever runs in Grist's iframe. */\nfunction isEmbedded(): boolean {\n return typeof window !== \"undefined\" && window.self !== window.top\n}\n\n/** The page's title and description, from the widget's declaration. */\nfunction PageMetadata({ metadata }: { metadata: GristWidgetAppProps[\"metadata\"] }) {\n useWidgetMetadata(metadata?.title ? { title: metadata.title, description: metadata.description ?? \"\" } : null)\n return null\n}\n\nfunction PassThrough({ children }: { children: ReactNode }) {\n return <>{children}</>\n}\n","import { useEffect, useState } from \"react\"\n\nimport {\n cardStyle,\n cardTextStyle,\n cardTitleStyle,\n codeStyle,\n eyebrowStyle,\n headingStyle,\n leadStyle,\n pageStyle,\n} from \"./tokens\"\nimport { widgetTitle } from \"./widget-title\"\n\n/**\n * Whether the widget has a demo page (`public/demo/`). The dev server serves\n * it at `demo/index.html` (Vite answers a bare `demo/` with the app's own page).\n */\nfunction useHasDemo(): boolean | null {\n const [has, setHas] = useState<boolean | null>(null)\n useEffect(() => {\n if (typeof fetch !== \"function\") return\n let live = true\n fetch(\"demo/demo.grist\", { method: \"HEAD\" })\n .then((res) => live && setHas(res.ok))\n .catch(() => live && setHas(false))\n return () => {\n live = false\n }\n }, [])\n return has\n}\n\n/**\n * Shown outside Grist on the dev server. Deliberately not a preview of the\n * widget: an emulator can't match real Grist. It points at the loops worth\n * trusting: the widget in real Grist running in this browser (the demo page,\n * grist-static, on this dev server's build, so edits reload in place),\n * `pnpm test`, and the `dev` branch's preview inside a Grist document.\n */\nexport function LocalDevPage() {\n const hasDemo = useHasDemo()\n return (\n <main style={pageStyle}>\n <p style={eyebrowStyle}>{widgetTitle()} · local dev</p>\n <h1 style={headingStyle}>Not embedded in Grist</h1>\n <p style={leadStyle}>This is the local dev server: there's no Grist here to render the widget against.</p>\n\n <section style={cardStyle}>\n <h2 style={cardTitleStyle}>Open it in Grist, in this browser</h2>\n {hasDemo === false ? (\n <p style={cardTextStyle}>\n Run <code style={codeStyle}>pnpm demo --save</code> once (it needs Docker): it writes{\" \"}\n <code style={codeStyle}>public/demo/</code>, a document for this widget. Then{\" \"}\n <a href=\"demo/index.html\">demo/index.html</a> opens it in real Grist, running in the browser, on this dev server's\n widget: edits reload in place.\n </p>\n ) : (\n <p style={cardTextStyle}>\n <a href=\"demo/index.html\">Open demo/index.html</a>: real Grist running in the browser (no server, nothing saved), with\n this dev server's widget, so edits reload in place. Its document is{\" \"}\n <code style={codeStyle}>public/demo/demo.grist</code>: after changing the declared columns, run{\" \"}\n <code style={codeStyle}>pnpm demo --save</code> (Docker) to regenerate it.\n </p>\n )}\n </section>\n\n <section style={cardStyle}>\n <h2 style={cardTitleStyle}>Check it against real Grist</h2>\n <p style={cardTextStyle}>\n Push to <code style={codeStyle}>dev</code>, then open its <code style={codeStyle}>/dev/</code> URL in a\n custom widget of a real Grist document: later pushes reload it there within seconds. Locally,{\" \"}\n <code style={codeStyle}>pnpm test</code> runs the widget against an emulated Grist.\n </p>\n </section>\n\n <section style={cardStyle}>\n <h2 style={cardTitleStyle}>Starting from another example?</h2>\n <p style={cardTextStyle}>\n <code style={codeStyle}>npx create-grist-widget list</code> shows the examples, and{\" \"}\n <code style={codeStyle}>npx create-grist-widget use <example></code> swaps this widget for one.\n </p>\n </section>\n </main>\n )\n}\n","import type { CSSProperties } from \"react\"\n\n/**\n * The chrome's colours and radius: shadcn's CSS variables, so the pages\n * follow a widget's theme (light, dark, its own palette) without depending\n * on its Tailwind config, with neutral fallbacks for a widget without them.\n * Same approach as GristBoundaryScreen.\n */\nconst token = (name: string, fallback: string) => `var(--${name}, ${fallback})`\n\nexport const tokens = {\n background: token(\"background\", \"#ffffff\"),\n foreground: token(\"foreground\", \"#0a0a0a\"),\n card: token(\"card\", \"#ffffff\"),\n mutedForeground: token(\"muted-foreground\", \"#737373\"),\n border: token(\"border\", \"#e5e5e5\"),\n primary: token(\"primary\", \"#171717\"),\n primaryForeground: token(\"primary-foreground\", \"#fafafa\"),\n destructive: token(\"destructive\", \"#dc2626\"),\n radius: token(\"radius\", \"0.625rem\"),\n} as const\n\nexport const pageStyle: CSSProperties = {\n boxSizing: \"border-box\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n maxWidth: \"28rem\",\n minHeight: \"100%\",\n margin: \"0 auto\",\n padding: \"4rem 1.5rem\",\n color: tokens.foreground,\n font: \"inherit\",\n}\n\nexport const eyebrowStyle: CSSProperties = {\n margin: \"0 0 0.5rem\",\n textAlign: \"center\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n color: tokens.mutedForeground,\n}\n\nexport const headingStyle: CSSProperties = {\n margin: 0,\n textAlign: \"center\",\n fontSize: \"1.5rem\",\n fontWeight: 600,\n letterSpacing: \"-0.01em\",\n}\n\nexport const leadStyle: CSSProperties = {\n margin: \"0.75rem auto 2rem\",\n maxWidth: \"24rem\",\n textAlign: \"center\",\n fontSize: \"0.875rem\",\n lineHeight: 1.5,\n color: tokens.mutedForeground,\n}\n\nexport const cardStyle: CSSProperties = {\n boxSizing: \"border-box\",\n marginBottom: \"1rem\",\n padding: \"1.25rem\",\n border: `1px solid ${tokens.border}`,\n borderRadius: tokens.radius,\n background: tokens.card,\n}\n\nexport const cardTitleStyle: CSSProperties = {\n margin: \"0 0 0.375rem\",\n fontSize: \"1rem\",\n fontWeight: 600,\n}\n\nexport const cardTextStyle: CSSProperties = {\n margin: 0,\n fontSize: \"0.875rem\",\n lineHeight: 1.5,\n color: tokens.mutedForeground,\n}\n\nexport const codeStyle: CSSProperties = {\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\n fontSize: \"0.8125rem\",\n color: tokens.foreground,\n}\n","/**\n * The widget's name for the pages shown outside Grist: the page title,\n * which `create-grist-widget` sets to the widget's title in `index.html`.\n */\nexport function widgetTitle(): string {\n const title = typeof document === \"undefined\" ? \"\" : document.title.trim()\n return title || \"Grist widget\"\n}\n","import { useState, type CSSProperties } from \"react\"\n\nimport { deployChannelLabel, parseDeployPath, type DeployChannel } from \"../deploy-path\"\nimport { useReleasedVersions } from \"../released-versions\"\nimport {\n cardStyle,\n cardTextStyle,\n cardTitleStyle,\n codeStyle,\n eyebrowStyle,\n headingStyle,\n leadStyle,\n pageStyle,\n tokens,\n} from \"./tokens\"\nimport { widgetTitle } from \"./widget-title\"\n\n/**\n * Shown outside Grist on a deployed URL (`/latest/`, `/dev/`, `/v<version>/`\n * or the root): which build this is, links to the others, and the URL to\n * paste into a Grist custom widget.\n */\nexport function ReleasePage({ channel }: { channel: DeployChannel }) {\n const label = deployChannelLabel(channel)\n return (\n <main style={pageStyle}>\n <p style={eyebrowStyle}>\n {widgetTitle()} · {label}\n </p>\n <h1 style={headingStyle}>Grist isn't loaded here</h1>\n <p style={leadStyle}>\n This is the <code style={codeStyle}>{label}</code> build, opened outside Grist. Hover a version to see when it\n was released.\n </p>\n <ChannelLinks current={channel} />\n <InstallUrl />\n </main>\n )\n}\n\nfunction ChannelLinks({ current }: { current: DeployChannel }) {\n const { basePath } = parseDeployPath(window.location.pathname)\n const { versions } = useReleasedVersions()\n const isCurrent = (channel: DeployChannel) =>\n channel.kind === current.kind && (channel.kind !== \"version\" || (current.kind === \"version\" && channel.version === current.version))\n\n return (\n <nav aria-label=\"Versions\" style={{ display: \"flex\", flexWrap: \"wrap\", justifyContent: \"center\", gap: \"0.5rem\", marginBottom: \"2rem\" }}>\n <ChannelLink href={`${basePath}latest/`} active={isCurrent({ kind: \"latest\" })}>\n latest\n </ChannelLink>\n <ChannelLink href={`${basePath}dev/`} active={isCurrent({ kind: \"dev\" })}>\n dev\n </ChannelLink>\n {versions?.map((v) => (\n <ChannelLink\n key={v.version}\n href={`${basePath}v${v.version}/`}\n title={formatDate(v.publishedAt)}\n active={isCurrent({ kind: \"version\", version: v.version })}\n >\n {`v${v.version}`}\n </ChannelLink>\n ))}\n </nav>\n )\n}\n\nfunction ChannelLink({\n href,\n title,\n active,\n children,\n}: {\n href: string\n title?: string\n active: boolean\n children: string\n}) {\n const style: CSSProperties = {\n padding: \"0.25rem 0.75rem\",\n border: `1px solid ${active ? tokens.primary : tokens.border}`,\n borderRadius: \"9999px\",\n background: active ? tokens.primary : \"transparent\",\n color: active ? tokens.primaryForeground : tokens.mutedForeground,\n fontSize: \"0.75rem\",\n fontWeight: 500,\n textDecoration: \"none\",\n }\n return (\n <a href={href} title={title} aria-current={active ? \"page\" : undefined} style={style}>\n {children}\n </a>\n )\n}\n\nfunction InstallUrl() {\n const url = window.location.href\n const [copied, setCopied] = useState(false)\n\n function copy() {\n void navigator.clipboard?.writeText(url).then(() => {\n setCopied(true)\n setTimeout(() => setCopied(false), 1500)\n })\n }\n\n return (\n <section style={cardStyle}>\n <h2 style={cardTitleStyle}>Use it as a Grist custom widget</h2>\n <p style={{ ...cardTextStyle, marginBottom: \"1rem\" }}>Paste this URL into a custom widget's URL field in Grist.</p>\n <div style={{ display: \"flex\", gap: \"0.5rem\" }}>\n <input\n readOnly\n value={url}\n aria-label=\"Widget URL\"\n onFocus={(event) => event.target.select()}\n style={{\n flex: 1,\n minWidth: 0,\n padding: \"0.5rem 0.75rem\",\n border: `1px solid ${tokens.border}`,\n borderRadius: tokens.radius,\n background: \"transparent\",\n color: tokens.foreground,\n font: \"inherit\",\n fontSize: \"0.875rem\",\n }}\n />\n <button\n type=\"button\"\n onClick={copy}\n style={{\n padding: \"0.5rem 1rem\",\n border: \"none\",\n borderRadius: tokens.radius,\n background: tokens.primary,\n color: tokens.primaryForeground,\n font: \"inherit\",\n fontSize: \"0.875rem\",\n fontWeight: 500,\n cursor: \"pointer\",\n }}\n >\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </div>\n </section>\n )\n}\n\nfunction formatDate(iso: string): string {\n const date = new Date(iso)\n return Number.isNaN(date.getTime())\n ? iso\n : date.toLocaleDateString(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" })\n}\n","/**\n * The alerts `mountGristWidget()` shows above a running widget: what needs the\n * user's attention in Grist (column mapping, access, a failed write, an\n * unlinked section). The blocking states (not connected, access refused,\n * required columns unmapped) are `<GristBoundary>`'s, not alerts.\n */\n\nimport { useNearestGristState } from \"../use-grist\"\nimport { deriveColumnMappingStatus, deriveStatus } from \"../runtime/derive\"\nimport { isAccessInsufficientError } from \"../runtime/api\"\nimport type { GristCellIssue } from \"../runtime/cells\"\nimport type { GristRuntimeState } from \"../runtime/runtime\"\nimport type { GristColumnsToMap, GristWidgetInteraction } from \"../types\"\nimport { parseGristWidgetLinkingInfo } from \"./grist-widget-interaction\"\n\nexport type GristSdkAlertKind =\n | \"column-mapping\"\n | \"mapping-pending\"\n | \"section-not-linked\"\n | \"source-not-wired\"\n | \"action-error\"\n | \"access-insufficient\"\n | \"map-back-skip\"\n | \"cell-issues\"\n\nexport type GristSdkAlertSeverity = \"info\" | \"warning\" | \"error\"\n\nexport type GristSdkAlertDescriptor = {\n id: string\n kind: GristSdkAlertKind\n severity: GristSdkAlertSeverity\n title: string\n ariaRole: \"status\" | \"alert\"\n message: string\n}\n\nexport type GristSdkAlertsOptions = {\n /** Added to the \"section not linked\" message, e.g. how to link in Grist. */\n sectionNotLinkedHint?: string\n}\n\nconst TITLE: Record<GristSdkAlertKind, string> = {\n \"column-mapping\": \"Column mapping\",\n \"mapping-pending\": \"Column mapping\",\n \"section-not-linked\": \"Widget linking\",\n \"source-not-wired\": \"Widget linking\",\n \"action-error\": \"Action error\",\n \"access-insufficient\": \"Access level\",\n \"map-back-skip\": \"Write skipped\",\n \"cell-issues\": \"Unexpected values\",\n}\n\nconst SEVERITY: Record<GristSdkAlertKind, GristSdkAlertSeverity> = {\n \"column-mapping\": \"warning\",\n \"mapping-pending\": \"info\",\n \"section-not-linked\": \"warning\",\n \"source-not-wired\": \"warning\",\n \"action-error\": \"error\",\n \"access-insufficient\": \"error\",\n \"map-back-skip\": \"warning\",\n \"cell-issues\": \"warning\",\n}\n\nconst alert = (kind: GristSdkAlertKind, message: string, ariaRole: \"status\" | \"alert\" = \"status\"): GristSdkAlertDescriptor => ({\n id: kind,\n kind,\n severity: SEVERITY[kind],\n title: TITLE[kind],\n ariaRole,\n message,\n})\n\n/** A widget section Grist says is linked to nothing. */\nexport function isWidgetSectionNotLinked(interaction: GristWidgetInteraction | null | undefined): boolean {\n const linking = parseGristWidgetLinkingInfo(interaction?.linking)\n return Boolean(linking) && linking!.asTarget == null\n}\n\n/** The alerts for a widget in `state`, pure (tested without React). */\nexport function getGristSdkAlerts(\n state: GristRuntimeState,\n widget: {\n columns?: GristColumnsToMap\n allowSelectBy?: boolean\n suppressAlerts?: readonly GristSdkAlertKind[]\n cellIssues?: readonly GristCellIssue[]\n },\n options: GristSdkAlertsOptions = {}\n): GristSdkAlertDescriptor[] {\n if (deriveStatus(state) !== \"ready\") return []\n const out: GristSdkAlertDescriptor[] = []\n\n const mapping = deriveColumnMappingStatus(widget.columns, state.mappings)\n if (mapping.pending) out.push(alert(\"mapping-pending\", \"Waiting for Grist to report column mappings.\"))\n else if (!mapping.ok) {\n const parts = [\"Column mapping is incomplete.\"]\n if (mapping.missing.length) parts.push(`Required columns: ${mapping.missing.join(\", \")}.`)\n if (mapping.emptyMultiples.length) parts.push(`Assign at least one sheet column for: ${mapping.emptyMultiples.join(\", \")}.`)\n out.push(alert(\"column-mapping\", parts.join(\" \")))\n }\n\n const failure = state.actionError ?? state.readError\n if (failure && isAccessInsufficientError(failure)) {\n out.push(\n alert(\n \"access-insufficient\",\n 'This operation requires \"Full document access\". Set the Access level to \"Full document access\" in the Custom widget configuration panel.',\n \"alert\"\n )\n )\n } else if (state.actionError) {\n out.push(alert(\"action-error\", state.actionError, \"alert\"))\n }\n\n if (state.mapBackSkipped.length) {\n const list = state.mapBackSkipped.join(\", \")\n out.push(alert(\"map-back-skip\", `mapBack skipped multi-mapped column${state.mapBackSkipped.length === 1 ? \"\" : \"s\"}: ${list}.`))\n }\n\n const cellIssues = widget.cellIssues ?? []\n if (cellIssues.length) {\n const first = cellIssues[0]!\n const where = first.column ? `${first.column} in row ${first.rowId}` : `row ${first.rowId}`\n const more = cellIssues.length > 1 ? ` (and ${cellIssues.length - 1} more)` : \"\"\n out.push(alert(\"cell-issues\", `${where}: ${first.message}${more}. Check the column's type, or the cell, in Grist.`))\n }\n\n const linking = parseGristWidgetLinkingInfo(state.interaction?.linking)\n if (widget.allowSelectBy) {\n if (linking && !linking.asSource) {\n out.push(\n alert(\n \"source-not-wired\",\n \"This widget is configured as a selector (allowSelectBy) but no other section is using it as a link source yet. Link another section to read from this widget.\"\n )\n )\n }\n } else if (isWidgetSectionNotLinked(state.interaction)) {\n const hint = options.sectionNotLinkedHint?.trim()\n out.push(\n alert(\n \"section-not-linked\",\n \"This widget section is not linked to a table or selector. Link it from the page settings so the selected row is sent to the widget.\" +\n (hint ? ` ${hint}` : \"\")\n )\n )\n }\n\n const suppressed = widget.suppressAlerts ?? []\n return out.filter((a) => !suppressed.includes(a.kind))\n}\n\n/** @internal The alerts for the widget under the nearest `<GristWidgetProvider>`; [] outside one. */\nexport function useGristSdkAlerts(options?: GristSdkAlertsOptions): GristSdkAlertDescriptor[] {\n const { connection, state, cellIssues } = useNearestGristState()\n if (!connection) return []\n const { columns, allowSelectBy, suppressAlerts } = connection.runtime.options\n return getGristSdkAlerts(state, { columns, allowSelectBy, suppressAlerts, cellIssues }, options)\n}\n","import type { CSSProperties, ReactNode } from \"react\"\n\nimport { useGristSdkAlerts, type GristSdkAlertDescriptor, type GristSdkAlertsOptions } from \"../../sdk/helpers/grist-sdk-alerts\"\nimport { tokens } from \"./tokens\"\n\nconst ALERT_OPTIONS: GristSdkAlertsOptions = {\n sectionNotLinkedHint:\n \"In Grist, select this widget section on the page, then use Link to connect a table, card, or chart view as the selector.\",\n}\n\n/**\n * The default `Alerts` of {@link mountGristWidget}: what needs the user's\n * attention (column mapping, access level, failed writes, an unlinked\n * section), above the widget. Renders nothing extra when all is well.\n */\nexport function WidgetAlerts({ children }: { children: ReactNode }) {\n const alerts = useGristSdkAlerts(ALERT_OPTIONS)\n if (alerts.length === 0) return <>{children}</>\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.75rem\" }}>\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.75rem\", padding: \"0.75rem 0.75rem 0\" }}>\n {alerts.map((alert) => (\n <WidgetAlert key={alert.id} alert={alert} />\n ))}\n </div>\n {children}\n </div>\n )\n}\n\nfunction WidgetAlert({ alert }: { alert: GristSdkAlertDescriptor }) {\n const linkIcon = alert.kind === \"section-not-linked\" || alert.kind === \"source-not-wired\"\n return (\n <div role={alert.ariaRole} style={{ ...alertStyle, ...SEVERITY[alert.severity] }}>\n <span aria-hidden style={{ flexShrink: 0, marginTop: \"0.125rem\" }}>\n {linkIcon ? <LinkIcon /> : <WarningIcon />}\n </span>\n <div style={{ minWidth: 0 }}>\n <p style={{ margin: 0, fontWeight: 500 }}>{alert.title}</p>\n <p style={{ margin: \"0.25rem 0 0\", color: tokens.mutedForeground }}>{alert.message}</p>\n </div>\n </div>\n )\n}\n\nconst alertStyle: CSSProperties = {\n boxSizing: \"border-box\",\n display: \"flex\",\n gap: \"0.75rem\",\n padding: \"0.75rem 1rem\",\n border: `1px solid ${tokens.border}`,\n borderRadius: tokens.radius,\n color: tokens.foreground,\n fontSize: \"0.875rem\",\n lineHeight: 1.5,\n}\n\nconst SEVERITY: Record<GristSdkAlertDescriptor[\"severity\"], CSSProperties> = {\n error: { borderColor: tokens.destructive, color: tokens.destructive },\n warning: { borderColor: \"rgb(245 158 11 / 0.4)\", background: \"rgb(245 158 11 / 0.1)\" },\n info: { background: \"transparent\" },\n}\n\nconst ICON = {\n width: 16,\n height: 16,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n} as const\n\nfunction WarningIcon() {\n return (\n <svg {...ICON}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </svg>\n )\n}\n\nfunction LinkIcon() {\n return (\n <svg {...ICON}>\n <path d=\"M9 17H7A5 5 0 0 1 7 7h2\" />\n <path d=\"M15 7h2a5 5 0 1 1 0 10h-2\" />\n <line x1=\"8\" x2=\"16\" y1=\"12\" y2=\"12\" />\n </svg>\n )\n}\n"],"mappings":"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","names":["useEffect","useState","useState","useEffect","useEffect","useState","jsx","useState","useEffect","useState","jsx","jsxs","useState","jsx","jsxs","alert","SEVERITY","Fragment","jsx","jsxs","LocalDevPage","ReleasePage"]}
|