@fasl-work/caos-app-shell 0.13.1 → 0.14.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 +9 -1
- package/bin/caos-shell-gate.mjs +3 -1
- package/dist/index.d.ts +180 -2
- package/dist/index.js +547 -122
- package/gate/areas.mjs +67 -0
- package/gate/inpage.mjs +151 -5
- package/gate/run.mjs +260 -4
- package/package.json +1 -1
- package/reserved-classes.json +44 -0
- package/styles.css +69 -5
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { APP_ROUTE, LANDING_ROUTE } from './chunk-33WTUQ7Q.js';
|
|
|
4
4
|
export { APP_ROUTE, LANDING_ROUTE, LANG_STORAGE_KEY, STANDARD_ROUTES, THEME_BOOT_SCRIPT, THEME_STORAGE_KEY } from './chunk-33WTUQ7Q.js';
|
|
5
5
|
import { createContext, Component, useState, useRef, useId, useEffect, useCallback, useContext, useMemo, useLayoutEffect, Children } from 'react';
|
|
6
6
|
import { useInRouterContext, NavLink, useLocation, Navigate, Link } from 'react-router';
|
|
7
|
-
import { Sun, Moon, Languages, Boxes, CodeXml, Globe, Briefcase, Info } from 'lucide-react';
|
|
7
|
+
import { Sun, Moon, Languages, Boxes, CodeXml, Globe, Briefcase, Info, ChevronLeft, ChevronRight, Maximize2, RotateCw } from 'lucide-react';
|
|
8
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
9
|
import katex from 'katex';
|
|
10
10
|
|
|
@@ -483,8 +483,9 @@ function WorkbenchShell({
|
|
|
483
483
|
overlays
|
|
484
484
|
] });
|
|
485
485
|
}
|
|
486
|
-
function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, stageLabel }) {
|
|
487
|
-
|
|
486
|
+
function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, stageLabel, landmark = true }) {
|
|
487
|
+
const Root = landmark ? "main" : "div";
|
|
488
|
+
return /* @__PURE__ */ jsxs(Root, { className: "caos-focus-shell", "data-focus-shell": "", children: [
|
|
488
489
|
/* @__PURE__ */ jsxs("section", { className: "caos-focus-stage", "aria-label": stageLabel, children: [
|
|
489
490
|
/* @__PURE__ */ jsx("div", { className: "caos-focus-instrument", children: stage }),
|
|
490
491
|
/* @__PURE__ */ jsxs("div", { className: "caos-focus-label", children: [
|
|
@@ -498,10 +499,10 @@ function FocusShell({ stage, rail, title, description, hud, onExit, exitLabel, s
|
|
|
498
499
|
/* @__PURE__ */ jsxs("div", { className: "caos-focus-actions", children: [
|
|
499
500
|
/* @__PURE__ */ jsx(LanguageToggle, {}),
|
|
500
501
|
/* @__PURE__ */ jsx(ThemeToggle, {}),
|
|
501
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: onExit, children: exitLabel })
|
|
502
|
+
/* @__PURE__ */ jsx("button", { type: "button", "data-focus-exit": "", onClick: onExit, children: exitLabel })
|
|
502
503
|
] })
|
|
503
504
|
] }),
|
|
504
|
-
/* @__PURE__ */ jsx("aside", { className: "caos-focus-rail", children: rail })
|
|
505
|
+
/* @__PURE__ */ jsx("aside", { className: "caos-focus-rail", "data-rail": "", children: rail })
|
|
505
506
|
] });
|
|
506
507
|
}
|
|
507
508
|
|
|
@@ -739,6 +740,17 @@ function CaseSelector(props) {
|
|
|
739
740
|
const visible = casesInSource(cases, source);
|
|
740
741
|
const groups = groupByCategory(visible);
|
|
741
742
|
const modified = cases.find((c) => c.id === modifiedFromId) ?? null;
|
|
743
|
+
const select = /* @__PURE__ */ jsx(
|
|
744
|
+
"select",
|
|
745
|
+
{
|
|
746
|
+
"data-control": "case",
|
|
747
|
+
className: "select cs-select",
|
|
748
|
+
"aria-label": ariaLabel ?? "Case selector",
|
|
749
|
+
value: selectedId,
|
|
750
|
+
onChange: (e) => onSelect(e.target.value),
|
|
751
|
+
children: groups.map((g) => /* @__PURE__ */ jsx("optgroup", { label: g.category || " ", children: g.cases.map((c) => /* @__PURE__ */ jsx("option", { value: c.id, disabled: c.disabled, title: caseTooltip(c) || void 0, children: c.name }, c.id)) }, g.category || "_"))
|
|
752
|
+
}
|
|
753
|
+
);
|
|
742
754
|
return /* @__PURE__ */ jsxs("div", { className: `cs${className ? ` ${className}` : ""}`, role: "group", "aria-label": ariaLabel ?? "Case selector", children: [
|
|
743
755
|
source && lanes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "cs-source", children: [
|
|
744
756
|
/* @__PURE__ */ jsx("span", { className: "cs-source-label", children: t2.source }),
|
|
@@ -756,17 +768,10 @@ function CaseSelector(props) {
|
|
|
756
768
|
)) })
|
|
757
769
|
] }),
|
|
758
770
|
source && source !== "synthetic" && /* @__PURE__ */ jsx("p", { className: "cs-locked banner", children: lockedNote ?? t2.lockedNote }),
|
|
759
|
-
layout === "select" && /* @__PURE__ */
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
className: "select cs-select",
|
|
764
|
-
"aria-label": ariaLabel ?? "Case selector",
|
|
765
|
-
value: selectedId,
|
|
766
|
-
onChange: (e) => onSelect(e.target.value),
|
|
767
|
-
children: groups.map((g) => /* @__PURE__ */ jsx("optgroup", { label: g.category || " ", children: g.cases.map((c) => /* @__PURE__ */ jsx("option", { value: c.id, disabled: c.disabled, title: caseTooltip(c) || void 0, children: c.name }, c.id)) }, g.category || "_"))
|
|
768
|
-
}
|
|
769
|
-
),
|
|
771
|
+
layout === "select" && (props.accessory ? /* @__PURE__ */ jsxs("div", { className: "cs-select-row", children: [
|
|
772
|
+
select,
|
|
773
|
+
props.accessory
|
|
774
|
+
] }) : select),
|
|
770
775
|
layout === "chips" && groups.map((g) => /* @__PURE__ */ jsxs("div", { className: "cs-group", role: "group", "aria-label": g.category || void 0, "data-control": "case", children: [
|
|
771
776
|
g.category && /* @__PURE__ */ jsx("span", { className: "cs-group-label", children: g.category }),
|
|
772
777
|
/* @__PURE__ */ jsx("div", { className: "cs-chips", children: g.cases.map((c) => {
|
|
@@ -792,6 +797,7 @@ function CaseSelector(props) {
|
|
|
792
797
|
);
|
|
793
798
|
}) })
|
|
794
799
|
] }, g.category || "_")),
|
|
800
|
+
layout === "chips" && props.accessory,
|
|
795
801
|
modified && /* @__PURE__ */ jsxs("div", { className: "cs-modified", children: [
|
|
796
802
|
/* @__PURE__ */ jsxs("span", { className: "badge accent", children: [
|
|
797
803
|
t2.modifiedPrefix,
|
|
@@ -805,6 +811,8 @@ function CaseSelector(props) {
|
|
|
805
811
|
function caseKindOf(c) {
|
|
806
812
|
return c.kind ?? "synthetic";
|
|
807
813
|
}
|
|
814
|
+
var ViewMemoryContext = createContext(null);
|
|
815
|
+
var memoryKey = (kind, ariaLabel, ids) => `${kind}|${ariaLabel ?? ""}|${ids.join(",")}`;
|
|
808
816
|
var CitationsContext = createContext({ list: [], byId: {} });
|
|
809
817
|
function CitationsProvider({ items, children }) {
|
|
810
818
|
const byId = {};
|
|
@@ -938,7 +946,12 @@ var MAX_PEER_TABS = 6;
|
|
|
938
946
|
function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
|
|
939
947
|
const baseId = useId();
|
|
940
948
|
const first = tabs[0]?.id ?? "";
|
|
941
|
-
const
|
|
949
|
+
const memory = useContext(ViewMemoryContext);
|
|
950
|
+
const mkey = memoryKey("tabs", ariaLabel, tabs.map((t2) => t2.id));
|
|
951
|
+
const [own, setOwn] = useState(() => {
|
|
952
|
+
const kept = memory?.get(mkey);
|
|
953
|
+
return kept && tabs.some((t2) => t2.id === kept) ? kept : initial ?? first;
|
|
954
|
+
});
|
|
942
955
|
const active = value ?? own;
|
|
943
956
|
const rowRef = useRef(null);
|
|
944
957
|
useOverflowFade(rowRef, ".tab.active", [active, tabs.length]);
|
|
@@ -947,6 +960,7 @@ function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
|
|
|
947
960
|
}
|
|
948
961
|
function select(id) {
|
|
949
962
|
if (value === void 0) setOwn(id);
|
|
963
|
+
memory?.set(mkey, id);
|
|
950
964
|
onChange?.(id);
|
|
951
965
|
}
|
|
952
966
|
function onKeyDown(e, idx) {
|
|
@@ -1003,7 +1017,12 @@ function Tabs({ tabs, initial, value, onChange, ariaLabel }) {
|
|
|
1003
1017
|
function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation = "horizontal" }) {
|
|
1004
1018
|
const baseId = useId();
|
|
1005
1019
|
const first = tabs[0]?.id ?? "";
|
|
1006
|
-
const
|
|
1020
|
+
const memory = useContext(ViewMemoryContext);
|
|
1021
|
+
const mkey = memoryKey("subtabs", ariaLabel, tabs.map((t2) => t2.id));
|
|
1022
|
+
const [own, setOwn] = useState(() => {
|
|
1023
|
+
const kept = memory?.get(mkey);
|
|
1024
|
+
return kept && tabs.some((t2) => t2.id === kept) ? kept : initial ?? first;
|
|
1025
|
+
});
|
|
1007
1026
|
const active = value ?? own;
|
|
1008
1027
|
const vertical = orientation === "vertical";
|
|
1009
1028
|
const rowRef = useRef(null);
|
|
@@ -1013,6 +1032,7 @@ function SubTabs({ tabs, initial, value, onChange, ariaLabel, orientation = "hor
|
|
|
1013
1032
|
}
|
|
1014
1033
|
function select(id) {
|
|
1015
1034
|
if (value === void 0) setOwn(id);
|
|
1035
|
+
memory?.set(mkey, id);
|
|
1016
1036
|
onChange?.(id);
|
|
1017
1037
|
}
|
|
1018
1038
|
function onKeyDown(e, idx) {
|
|
@@ -2125,6 +2145,216 @@ var ONWARD_LINKS = {
|
|
|
2125
2145
|
technical: { en: "How the work explores this problem", es: "C\xF3mo explora el trabajo este problema" },
|
|
2126
2146
|
app: { en: "Open the App", es: "Abrir la App" }
|
|
2127
2147
|
};
|
|
2148
|
+
var useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
2149
|
+
var FLIP_LIMITS = {
|
|
2150
|
+
title: { en: 8, es: 10 },
|
|
2151
|
+
back: { en: 40, es: 50 }
|
|
2152
|
+
};
|
|
2153
|
+
var FLIP_TEXT = {
|
|
2154
|
+
show: { en: "Show details", es: "Mostrar detalles" },
|
|
2155
|
+
hide: { en: "Hide details", es: "Ocultar detalles" }
|
|
2156
|
+
};
|
|
2157
|
+
function FlipCard({ title, label, children, id, defaultOpen = false, open: controlled, onOpenChange, className, lang: forced }) {
|
|
2158
|
+
const stored = useShellLang();
|
|
2159
|
+
const lang = forced ?? stored;
|
|
2160
|
+
const auto = useId().replace(/:/g, "");
|
|
2161
|
+
const base = id ?? `flip-${auto}`;
|
|
2162
|
+
const backId = `${base}-back`;
|
|
2163
|
+
const [own, setOwn] = useState(defaultOpen);
|
|
2164
|
+
const open = controlled ?? own;
|
|
2165
|
+
const front = useRef(null);
|
|
2166
|
+
const back = useRef(null);
|
|
2167
|
+
const byUser = useRef(false);
|
|
2168
|
+
const frontFace = useRef(null);
|
|
2169
|
+
const backFace = useRef(null);
|
|
2170
|
+
useIsoLayoutEffect(() => {
|
|
2171
|
+
frontFace.current?.toggleAttribute("inert", open);
|
|
2172
|
+
backFace.current?.toggleAttribute("inert", !open);
|
|
2173
|
+
}, [open]);
|
|
2174
|
+
useEffect(() => {
|
|
2175
|
+
if (!byUser.current) return;
|
|
2176
|
+
(open ? back : front).current?.focus({ preventScroll: true });
|
|
2177
|
+
}, [open]);
|
|
2178
|
+
function turn(next) {
|
|
2179
|
+
byUser.current = true;
|
|
2180
|
+
if (controlled === void 0) setOwn(next);
|
|
2181
|
+
onOpenChange?.(next);
|
|
2182
|
+
}
|
|
2183
|
+
function onKeyDown(e) {
|
|
2184
|
+
if (e.key === "Escape" && open) {
|
|
2185
|
+
e.preventDefault();
|
|
2186
|
+
e.stopPropagation();
|
|
2187
|
+
turn(false);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
const icon = /* @__PURE__ */ jsx(RotateCw, { size: 14, "aria-hidden": "true", className: "caos-flip-icon" });
|
|
2191
|
+
return /* @__PURE__ */ jsx("div", { className: ["caos-flip", className].filter(Boolean).join(" "), "data-flip-card": "", "data-flip-open": open ? "true" : "false", onKeyDown, children: /* @__PURE__ */ jsxs("div", { className: "caos-flip-inner", children: [
|
|
2192
|
+
/* @__PURE__ */ jsx("div", { className: "caos-flip-face caos-flip-front", "aria-hidden": open ? true : void 0, ref: frontFace, children: /* @__PURE__ */ jsxs(
|
|
2193
|
+
"button",
|
|
2194
|
+
{
|
|
2195
|
+
type: "button",
|
|
2196
|
+
className: "caos-flip-toggle",
|
|
2197
|
+
"aria-expanded": open,
|
|
2198
|
+
"aria-controls": backId,
|
|
2199
|
+
"data-flip-toggle": "front",
|
|
2200
|
+
ref: front,
|
|
2201
|
+
onClick: () => turn(true),
|
|
2202
|
+
children: [
|
|
2203
|
+
/* @__PURE__ */ jsx("span", { className: "caos-flip-title", "data-flip-title": "", children: title }),
|
|
2204
|
+
icon,
|
|
2205
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: FLIP_TEXT.show[lang] })
|
|
2206
|
+
]
|
|
2207
|
+
}
|
|
2208
|
+
) }),
|
|
2209
|
+
/* @__PURE__ */ jsxs("div", { className: "caos-flip-face caos-flip-back", id: backId, "data-flip-back": "", "aria-hidden": open ? void 0 : true, ref: backFace, children: [
|
|
2210
|
+
/* @__PURE__ */ jsxs(
|
|
2211
|
+
"button",
|
|
2212
|
+
{
|
|
2213
|
+
type: "button",
|
|
2214
|
+
className: "caos-flip-toggle caos-flip-return",
|
|
2215
|
+
"aria-expanded": open,
|
|
2216
|
+
"aria-controls": backId,
|
|
2217
|
+
"data-flip-toggle": "back",
|
|
2218
|
+
ref: back,
|
|
2219
|
+
onClick: () => turn(false),
|
|
2220
|
+
children: [
|
|
2221
|
+
label ? /* @__PURE__ */ jsx("span", { className: "caos-flip-return-title", children: label }) : /* @__PURE__ */ jsx("span", {}),
|
|
2222
|
+
icon,
|
|
2223
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: FLIP_TEXT.hide[lang] })
|
|
2224
|
+
]
|
|
2225
|
+
}
|
|
2226
|
+
),
|
|
2227
|
+
/* @__PURE__ */ jsx("div", { className: "caos-flip-content", "data-flip-content": "", children })
|
|
2228
|
+
] })
|
|
2229
|
+
] }) });
|
|
2230
|
+
}
|
|
2231
|
+
var DECK_TEXT = {
|
|
2232
|
+
previous: { en: "Previous", es: "Anterior" },
|
|
2233
|
+
next: { en: "Next", es: "Siguiente" },
|
|
2234
|
+
of: { en: "of", es: "de" },
|
|
2235
|
+
to: { en: "to", es: "a" }
|
|
2236
|
+
};
|
|
2237
|
+
var MIN_CARD_REM = 15;
|
|
2238
|
+
var GAP_REM = 0.9;
|
|
2239
|
+
var SWIPE_PX = 50;
|
|
2240
|
+
function perPageFor(width, minCard, gap) {
|
|
2241
|
+
if (!(width > 0)) return 1;
|
|
2242
|
+
return Math.max(1, Math.floor((width + gap) / (minCard + gap)));
|
|
2243
|
+
}
|
|
2244
|
+
function deckWindow(n, per, start) {
|
|
2245
|
+
const p = Math.max(1, Math.floor(per));
|
|
2246
|
+
if (p >= n) return { start: 0, end: n };
|
|
2247
|
+
const s = Math.min(Math.max(0, start), n - p);
|
|
2248
|
+
return { start: s, end: s + p };
|
|
2249
|
+
}
|
|
2250
|
+
function deckPosition(start, shown, n, lang) {
|
|
2251
|
+
const o = DECK_TEXT.of[lang];
|
|
2252
|
+
if (shown <= 1) return `${start + 1} ${o} ${n}`;
|
|
2253
|
+
return `${start + 1} ${DECK_TEXT.to[lang]} ${start + shown} ${o} ${n}`;
|
|
2254
|
+
}
|
|
2255
|
+
var useIsoLayoutEffect2 = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
2256
|
+
function CardDeck({ cards, flip = false, carouselAbove = 4, ariaLabel, className, lang: forced }) {
|
|
2257
|
+
const stored = useShellLang();
|
|
2258
|
+
const lang = forced ?? stored;
|
|
2259
|
+
const n = cards.length;
|
|
2260
|
+
const carousel = n > carouselAbove;
|
|
2261
|
+
const rootRef = useRef(null);
|
|
2262
|
+
const [per, setPer] = useState(1);
|
|
2263
|
+
const [start, setStart] = useState(0);
|
|
2264
|
+
const [moved, setMoved] = useState(false);
|
|
2265
|
+
const touch = useRef(null);
|
|
2266
|
+
useIsoLayoutEffect2(() => {
|
|
2267
|
+
const root = rootRef.current;
|
|
2268
|
+
if (!carousel || !root || typeof ResizeObserver === "undefined") return void 0;
|
|
2269
|
+
const measure = () => {
|
|
2270
|
+
const rem = parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
2271
|
+
setPer(perPageFor(root.clientWidth, MIN_CARD_REM * rem, GAP_REM * rem));
|
|
2272
|
+
};
|
|
2273
|
+
measure();
|
|
2274
|
+
const ro = new ResizeObserver(measure);
|
|
2275
|
+
ro.observe(root);
|
|
2276
|
+
return () => ro.disconnect();
|
|
2277
|
+
}, [carousel, n]);
|
|
2278
|
+
const win = deckWindow(n, carousel ? per : n, start);
|
|
2279
|
+
const shown = win.end - win.start;
|
|
2280
|
+
const controls = carousel && shown < n;
|
|
2281
|
+
const atStart = win.start <= 0;
|
|
2282
|
+
const atEnd = win.end >= n;
|
|
2283
|
+
function go(to, step = false) {
|
|
2284
|
+
if (!controls) return;
|
|
2285
|
+
setMoved(true);
|
|
2286
|
+
setStart(deckWindow(n, per, step ? win.start + to : to).start);
|
|
2287
|
+
}
|
|
2288
|
+
const page = () => Math.max(1, per);
|
|
2289
|
+
useEffect(() => {
|
|
2290
|
+
const root = rootRef.current;
|
|
2291
|
+
const a = document.activeElement;
|
|
2292
|
+
if (!root || !a || !root.contains(a) || a.checkVisibility?.({ checkVisibilityCSS: true }) !== false) return;
|
|
2293
|
+
(root.querySelector('[data-deck-card]:not([hidden]) [data-flip-card][data-flip-open="false"] [data-flip-toggle="front"], [data-deck-card]:not([hidden]) [data-flip-card][data-flip-open="true"] [data-flip-toggle="back"]') ?? root.querySelector("[data-deck-next]"))?.focus({ preventScroll: true });
|
|
2294
|
+
}, [win.start, win.end]);
|
|
2295
|
+
function onKeyDown(e) {
|
|
2296
|
+
if (!controls || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return;
|
|
2297
|
+
const t2 = e.target;
|
|
2298
|
+
if (t2.closest('input, select, textarea, [contenteditable="true"]')) return;
|
|
2299
|
+
if (e.key === "ArrowRight") go(1, true);
|
|
2300
|
+
else if (e.key === "ArrowLeft") go(-1, true);
|
|
2301
|
+
else if (e.key === "Home") go(0);
|
|
2302
|
+
else if (e.key === "End") go(n);
|
|
2303
|
+
else return;
|
|
2304
|
+
e.preventDefault();
|
|
2305
|
+
}
|
|
2306
|
+
function onTouchStart(e) {
|
|
2307
|
+
const t2 = e.touches[0];
|
|
2308
|
+
touch.current = t2 ? { x: t2.clientX, y: t2.clientY } : null;
|
|
2309
|
+
}
|
|
2310
|
+
function onTouchEnd(e) {
|
|
2311
|
+
const from = touch.current;
|
|
2312
|
+
const t2 = e.changedTouches[0];
|
|
2313
|
+
touch.current = null;
|
|
2314
|
+
if (!from || !t2 || !controls) return;
|
|
2315
|
+
const dx = t2.clientX - from.x;
|
|
2316
|
+
const dy = t2.clientY - from.y;
|
|
2317
|
+
if (Math.abs(dx) >= SWIPE_PX && Math.abs(dx) > 2 * Math.abs(dy)) go(dx < 0 ? page() : -page(), true);
|
|
2318
|
+
}
|
|
2319
|
+
return /* @__PURE__ */ jsxs(
|
|
2320
|
+
"div",
|
|
2321
|
+
{
|
|
2322
|
+
ref: rootRef,
|
|
2323
|
+
className: "caos-deck",
|
|
2324
|
+
"data-card-deck": "",
|
|
2325
|
+
"data-deck-mode": carousel ? "carousel" : "row",
|
|
2326
|
+
"data-deck-per": carousel ? Math.min(per, n) : n,
|
|
2327
|
+
"data-deck-flip": flip ? "" : void 0,
|
|
2328
|
+
"data-deck-moved": moved ? "" : void 0,
|
|
2329
|
+
role: carousel ? "group" : void 0,
|
|
2330
|
+
"aria-roledescription": carousel ? "carousel" : void 0,
|
|
2331
|
+
"aria-label": carousel ? ariaLabel : void 0,
|
|
2332
|
+
onKeyDown: carousel ? onKeyDown : void 0,
|
|
2333
|
+
children: [
|
|
2334
|
+
/* @__PURE__ */ jsx(
|
|
2335
|
+
"ul",
|
|
2336
|
+
{
|
|
2337
|
+
className: ["caos-deck-track", className].filter(Boolean).join(" "),
|
|
2338
|
+
"data-deck-track": "",
|
|
2339
|
+
"aria-label": carousel ? void 0 : ariaLabel,
|
|
2340
|
+
style: carousel ? { "--deck-per": Math.min(per, n) } : void 0,
|
|
2341
|
+
onTouchStart: carousel ? onTouchStart : void 0,
|
|
2342
|
+
onTouchEnd: carousel ? onTouchEnd : void 0,
|
|
2343
|
+
children: cards.map((c, i) => /* @__PURE__ */ jsx("li", { className: "caos-deck-card", "data-deck-card": "", hidden: carousel && (i < win.start || i >= win.end), ...c.attrs, children: flip ? /* @__PURE__ */ jsx(FlipCard, { title: c.title, label: c.label, lang, children: c.body }) : /* @__PURE__ */ jsxs("div", { className: "caos-card", children: [
|
|
2344
|
+
/* @__PURE__ */ jsx("strong", { className: "caos-card-title", children: c.title }),
|
|
2345
|
+
c.body
|
|
2346
|
+
] }) }, c.id ?? i))
|
|
2347
|
+
}
|
|
2348
|
+
),
|
|
2349
|
+
controls && /* @__PURE__ */ jsxs("div", { className: "caos-deck-bar", children: [
|
|
2350
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "caos-deck-btn", "data-deck-prev": "", "aria-label": DECK_TEXT.previous[lang], "aria-disabled": atStart, onClick: () => !atStart && go(-page(), true), children: /* @__PURE__ */ jsx(ChevronLeft, { size: 18, "aria-hidden": "true" }) }),
|
|
2351
|
+
/* @__PURE__ */ jsx("span", { className: "caos-deck-position", "data-deck-position": "", "aria-live": "polite", children: deckPosition(win.start, shown, n, lang) }),
|
|
2352
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "caos-deck-btn", "data-deck-next": "", "aria-label": DECK_TEXT.next[lang], "aria-disabled": atEnd, onClick: () => !atEnd && go(page(), true), children: /* @__PURE__ */ jsx(ChevronRight, { size: 18, "aria-hidden": "true" }) })
|
|
2353
|
+
] })
|
|
2354
|
+
]
|
|
2355
|
+
}
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2128
2358
|
var UI = {
|
|
2129
2359
|
bearers: { en: "Who bears it", es: "Qui\xE9n lo soporta" },
|
|
2130
2360
|
cost: { en: "Cost", es: "Costo" },
|
|
@@ -2188,6 +2418,9 @@ function cleanSvg(text) {
|
|
|
2188
2418
|
const root = doc.documentElement;
|
|
2189
2419
|
if (!root || root.localName !== "svg" || doc.querySelector("parsererror")) return null;
|
|
2190
2420
|
for (const bad of Array.from(doc.querySelectorAll("script, foreignObject"))) bad.remove();
|
|
2421
|
+
for (const anim of Array.from(doc.querySelectorAll("animate, set"))) {
|
|
2422
|
+
if (/^(xlink:)?href$|^on/i.test(anim.getAttribute("attributeName") ?? "")) anim.remove();
|
|
2423
|
+
}
|
|
2191
2424
|
for (const el of Array.from(doc.querySelectorAll("*"))) {
|
|
2192
2425
|
for (const attr of Array.from(el.attributes)) {
|
|
2193
2426
|
if (/^on/i.test(attr.name) || /^(xlink:)?href$/i.test(attr.name) && /^\s*javascript:/i.test(attr.value)) el.removeAttribute(attr.name);
|
|
@@ -2199,10 +2432,41 @@ function cleanSvg(text) {
|
|
|
2199
2432
|
if (!root.getAttribute("role")) root.setAttribute("role", "img");
|
|
2200
2433
|
return new XMLSerializer().serializeToString(root);
|
|
2201
2434
|
}
|
|
2435
|
+
function smilEnd(svg) {
|
|
2436
|
+
const secs = (v) => {
|
|
2437
|
+
const m = /^\s*(\d*\.?\d+)\s*(ms|s)?\s*$/.exec(v ?? "");
|
|
2438
|
+
return m ? Number(m[1]) / (m[2] === "ms" ? 1e3 : 1) : 0;
|
|
2439
|
+
};
|
|
2440
|
+
let end = 0;
|
|
2441
|
+
for (const a of Array.from(svg.querySelectorAll("animate, animateTransform, animateMotion, set"))) {
|
|
2442
|
+
end = Math.max(end, secs(a.getAttribute("begin")) + secs(a.getAttribute("dur")));
|
|
2443
|
+
}
|
|
2444
|
+
return end;
|
|
2445
|
+
}
|
|
2446
|
+
function useStillSmil(host, svg) {
|
|
2447
|
+
useEffect(() => {
|
|
2448
|
+
const el = host.current?.querySelector("svg");
|
|
2449
|
+
if (!svg || !el || typeof matchMedia === "undefined" || !el.querySelector("animate, animateTransform, animateMotion, set")) return void 0;
|
|
2450
|
+
const mq = matchMedia("(prefers-reduced-motion: reduce)");
|
|
2451
|
+
const apply = () => {
|
|
2452
|
+
if (mq.matches) {
|
|
2453
|
+
el.setCurrentTime(smilEnd(el));
|
|
2454
|
+
el.pauseAnimations();
|
|
2455
|
+
} else {
|
|
2456
|
+
el.unpauseAnimations();
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
apply();
|
|
2460
|
+
mq.addEventListener("change", apply);
|
|
2461
|
+
return () => mq.removeEventListener("change", apply);
|
|
2462
|
+
}, [host, svg]);
|
|
2463
|
+
}
|
|
2202
2464
|
function Illustration({ name, spec, base, lang }) {
|
|
2203
2465
|
const file = typeof spec.file === "string" ? spec.file : spec.file[lang];
|
|
2204
2466
|
const url = /^([a-z]+:)?\/\//i.test(file) || file.startsWith("/") ? file : `${base}${file}`;
|
|
2205
2467
|
const [loaded, setLoaded] = useState({ status: "loading" });
|
|
2468
|
+
const art = useRef(null);
|
|
2469
|
+
useStillSmil(art, loaded.status === "ready" ? loaded.svg : null);
|
|
2206
2470
|
useEffect(() => {
|
|
2207
2471
|
let live = true;
|
|
2208
2472
|
setLoaded({ status: "loading" });
|
|
@@ -2229,7 +2493,7 @@ function Illustration({ name, spec, base, lang }) {
|
|
|
2229
2493
|
"data-exec-file": file,
|
|
2230
2494
|
"data-state": loaded.status === "ready" ? void 0 : loaded.status,
|
|
2231
2495
|
children: [
|
|
2232
|
-
loaded.status === "ready" ? /* @__PURE__ */ jsx("div", { className: "caos-exec-art", dangerouslySetInnerHTML: { __html: loaded.svg } }) : loaded.status === "error" ? /* @__PURE__ */ jsxs("p", { className: "caos-exec-fig-error", role: "alert", children: [
|
|
2496
|
+
loaded.status === "ready" ? /* @__PURE__ */ jsx("div", { className: "caos-exec-art", ref: art, dangerouslySetInnerHTML: { __html: loaded.svg } }) : loaded.status === "error" ? /* @__PURE__ */ jsxs("p", { className: "caos-exec-fig-error", role: "alert", children: [
|
|
2233
2497
|
t(UI.missing, lang),
|
|
2234
2498
|
": ",
|
|
2235
2499
|
/* @__PURE__ */ jsx("code", { children: file }),
|
|
@@ -2311,19 +2575,32 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
|
|
|
2311
2575
|
art("pain"),
|
|
2312
2576
|
/* @__PURE__ */ jsx("ul", { className: "caos-exec-figures", children: d.pain.figures.map((f, i) => /* @__PURE__ */ jsx(Figure2, { f, path: `pain.figures[${i}]`, kind: "pain.figures" }, i)) }),
|
|
2313
2577
|
/* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.bearers, lang) }),
|
|
2314
|
-
/* @__PURE__ */ jsx(
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2578
|
+
/* @__PURE__ */ jsx(
|
|
2579
|
+
CardDeck,
|
|
2580
|
+
{
|
|
2581
|
+
flip: true,
|
|
2582
|
+
ariaLabel: t(UI.bearers, lang),
|
|
2583
|
+
className: "caos-exec-cards",
|
|
2584
|
+
cards: d.pain.bearers.map((b, i) => ({
|
|
2585
|
+
id: i,
|
|
2586
|
+
title: field(`pain.bearers[${i}].who`, b.who),
|
|
2587
|
+
label: t(b.who, lang),
|
|
2588
|
+
attrs: { "data-exec-item": "pain.bearers" },
|
|
2589
|
+
body: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2590
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2591
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.cost, lang) }),
|
|
2592
|
+
" ",
|
|
2593
|
+
field(`pain.bearers[${i}].cost`, b.cost)
|
|
2594
|
+
] }),
|
|
2595
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2596
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.measure, lang) }),
|
|
2597
|
+
" ",
|
|
2598
|
+
field(`pain.bearers[${i}].measure`, b.measure)
|
|
2599
|
+
] })
|
|
2600
|
+
] })
|
|
2601
|
+
}))
|
|
2602
|
+
}
|
|
2603
|
+
),
|
|
2327
2604
|
/* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.causes, lang) }),
|
|
2328
2605
|
/* @__PURE__ */ jsx("ul", { className: "caos-exec-causes", children: d.pain.causes.map((c, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-cause", "data-exec-item": "pain.causes", "data-exec-weight": c.weight, children: [
|
|
2329
2606
|
/* @__PURE__ */ jsxs("div", { className: "caos-exec-cause-head", children: [
|
|
@@ -2359,38 +2636,66 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
|
|
|
2359
2636
|
// 3. the current situation
|
|
2360
2637
|
/* @__PURE__ */ jsxs(Section, { n: 3, lang, keyMessage: d.situation.key_message, keyPath: "situation.key_message", children: [
|
|
2361
2638
|
/* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.roles, lang) }),
|
|
2362
|
-
/* @__PURE__ */ jsx(
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2639
|
+
/* @__PURE__ */ jsx(
|
|
2640
|
+
CardDeck,
|
|
2641
|
+
{
|
|
2642
|
+
flip: true,
|
|
2643
|
+
ariaLabel: t(UI.roles, lang),
|
|
2644
|
+
className: "caos-exec-cards",
|
|
2645
|
+
cards: d.situation.roles.map((r, i) => ({
|
|
2646
|
+
id: i,
|
|
2647
|
+
title: field(`situation.roles[${i}].role`, r.role),
|
|
2648
|
+
label: t(r.role, lang),
|
|
2649
|
+
attrs: { "data-exec-item": "situation.roles" },
|
|
2650
|
+
body: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2651
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2652
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.decides, lang) }),
|
|
2653
|
+
" ",
|
|
2654
|
+
field(`situation.roles[${i}].decides`, r.decides)
|
|
2655
|
+
] }),
|
|
2656
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2657
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.needs, lang) }),
|
|
2658
|
+
" ",
|
|
2659
|
+
field(`situation.roles[${i}].needs`, r.needs)
|
|
2660
|
+
] })
|
|
2661
|
+
] })
|
|
2662
|
+
}))
|
|
2663
|
+
}
|
|
2664
|
+
),
|
|
2375
2665
|
/* @__PURE__ */ jsx("p", { className: "caos-exec-interaction", children: field("situation.interaction", d.situation.interaction) }),
|
|
2376
2666
|
art("situation"),
|
|
2377
2667
|
/* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.sota, lang) }),
|
|
2378
|
-
/* @__PURE__ */ jsx(
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2668
|
+
/* @__PURE__ */ jsx(
|
|
2669
|
+
CardDeck,
|
|
2670
|
+
{
|
|
2671
|
+
flip: true,
|
|
2672
|
+
ariaLabel: t(UI.sota, lang),
|
|
2673
|
+
className: "caos-exec-cards",
|
|
2674
|
+
cards: d.situation.state_of_the_art.map((a, i) => ({
|
|
2675
|
+
id: i,
|
|
2676
|
+
title: field(`situation.state_of_the_art[${i}].approach`, a.approach),
|
|
2677
|
+
label: t(a.approach, lang),
|
|
2678
|
+
attrs: { "data-exec-item": "situation.state_of_the_art" },
|
|
2679
|
+
body: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2680
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2681
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.achieves, lang) }),
|
|
2682
|
+
" ",
|
|
2683
|
+
field(`situation.state_of_the_art[${i}].achieves`, a.achieves)
|
|
2684
|
+
] }),
|
|
2685
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2686
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.fallsShort, lang) }),
|
|
2687
|
+
" ",
|
|
2688
|
+
field(`situation.state_of_the_art[${i}].falls_short`, a.falls_short)
|
|
2689
|
+
] }),
|
|
2690
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2691
|
+
/* @__PURE__ */ jsx(Label, { children: t(UI.source, lang) }),
|
|
2692
|
+
" ",
|
|
2693
|
+
/* @__PURE__ */ jsx(Cite2, { id: a.source })
|
|
2694
|
+
] })
|
|
2695
|
+
] })
|
|
2696
|
+
}))
|
|
2697
|
+
}
|
|
2698
|
+
),
|
|
2394
2699
|
/* @__PURE__ */ jsxs("p", { className: "caos-exec-status", "data-exec-item": "situation.status", children: [
|
|
2395
2700
|
/* @__PURE__ */ jsx(Label, { children: field("situation.status.heading", d.situation.status.heading) }),
|
|
2396
2701
|
" ",
|
|
@@ -2434,11 +2739,24 @@ function ExecutiveViewBody({ data, base = "/", lang: forced, open = 1, onOpenTec
|
|
|
2434
2739
|
/* @__PURE__ */ jsx(Paragraphs, { list: d.proposal.paragraphs, path: "proposal.paragraphs" }),
|
|
2435
2740
|
art("proposal"),
|
|
2436
2741
|
/* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.fourCs, lang) }),
|
|
2437
|
-
/* @__PURE__ */ jsx(
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2742
|
+
/* @__PURE__ */ jsx(
|
|
2743
|
+
CardDeck,
|
|
2744
|
+
{
|
|
2745
|
+
flip: true,
|
|
2746
|
+
ariaLabel: t(UI.fourCs, lang),
|
|
2747
|
+
className: "caos-exec-cs",
|
|
2748
|
+
cards: d.proposal.four_cs.map((r, i) => ({
|
|
2749
|
+
id: i,
|
|
2750
|
+
title: /* @__PURE__ */ jsx("span", { "data-exec-field": `proposal.four_cs[${i}].c`, "data-exec-cname": "", children: t(FOUR_CS[i].name, lang) }),
|
|
2751
|
+
label: t(FOUR_CS[i].name, lang),
|
|
2752
|
+
attrs: { "data-exec-c": i, "data-exec-item": "proposal.four_cs" },
|
|
2753
|
+
body: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2754
|
+
/* @__PURE__ */ jsx("small", { className: "caos-exec-cdef", "data-exec-cdef": "", children: t(FOUR_CS[i].definition, lang) }),
|
|
2755
|
+
/* @__PURE__ */ jsx("span", { children: field(`proposal.four_cs[${i}].applies`, r.applies) })
|
|
2756
|
+
] })
|
|
2757
|
+
}))
|
|
2758
|
+
}
|
|
2759
|
+
),
|
|
2442
2760
|
/* @__PURE__ */ jsx("h3", { className: "caos-exec-sub", children: t(UI.actions, lang) }),
|
|
2443
2761
|
/* @__PURE__ */ jsx("ol", { className: "caos-exec-actions", children: d.proposal.actions.map((a, i) => /* @__PURE__ */ jsxs("li", { className: "caos-exec-card", "data-exec-item": "proposal.actions", children: [
|
|
2444
2762
|
/* @__PURE__ */ jsx("strong", { children: field(`proposal.actions[${i}].action`, a.action) }),
|
|
@@ -2523,6 +2841,13 @@ function replaceQueryParam(param, value) {
|
|
|
2523
2841
|
const next = withCaseParam(window.location.search, value, param);
|
|
2524
2842
|
window.history.replaceState(window.history.state, "", `${window.location.pathname}${next}${window.location.hash}`);
|
|
2525
2843
|
}
|
|
2844
|
+
function removeQueryParam(param) {
|
|
2845
|
+
if (typeof window === "undefined") return;
|
|
2846
|
+
const p = new URLSearchParams(window.location.search);
|
|
2847
|
+
p.delete(param);
|
|
2848
|
+
const q = p.toString();
|
|
2849
|
+
window.history.replaceState(window.history.state, "", `${window.location.pathname}${q ? `?${q}` : ""}${window.location.hash}`);
|
|
2850
|
+
}
|
|
2526
2851
|
function useUrlView(ids, param, value, onChange) {
|
|
2527
2852
|
const [own, setOwn] = useState(
|
|
2528
2853
|
() => param && typeof window !== "undefined" ? readCaseParam(window.location.search, param) : null
|
|
@@ -2787,7 +3112,7 @@ function BarChart({ title, data, axis, orientation = "horizontal", values = true
|
|
|
2787
3112
|
function isSections(rail) {
|
|
2788
3113
|
return Array.isArray(rail) && rail.every((s) => typeof s === "object" && s !== null && "id" in s && "content" in s);
|
|
2789
3114
|
}
|
|
2790
|
-
function
|
|
3115
|
+
function RailBody({ rail, railHead, railLabel }) {
|
|
2791
3116
|
const lang = useShellLang();
|
|
2792
3117
|
const sections = isSections(rail) ? rail : null;
|
|
2793
3118
|
const [active, setActive] = useState(sections?.[0]?.id ?? "");
|
|
@@ -2807,38 +3132,43 @@ function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
|
|
|
2807
3132
|
document.getElementById(`${baseId}-rs-${target.id}`)?.focus();
|
|
2808
3133
|
}
|
|
2809
3134
|
}
|
|
3135
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3136
|
+
railHead && /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-head", children: railHead }),
|
|
3137
|
+
sections ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3138
|
+
/* @__PURE__ */ jsx("div", { className: "caos-wb-rail-sections", role: "tablist", "aria-label": label, ref: rowRef, children: sections.map((s, idx) => /* @__PURE__ */ jsx(
|
|
3139
|
+
"button",
|
|
3140
|
+
{
|
|
3141
|
+
type: "button",
|
|
3142
|
+
role: "tab",
|
|
3143
|
+
id: `${baseId}-rs-${s.id}`,
|
|
3144
|
+
"aria-selected": s.id === current?.id,
|
|
3145
|
+
"aria-controls": `${baseId}-rp-${s.id}`,
|
|
3146
|
+
tabIndex: s.id === current?.id ? 0 : -1,
|
|
3147
|
+
className: s.id === current?.id ? "chip on" : "chip",
|
|
3148
|
+
onClick: () => setActive(s.id),
|
|
3149
|
+
onKeyDown: (e) => onKeyDown(e, idx),
|
|
3150
|
+
children: pick(s.label, lang)
|
|
3151
|
+
},
|
|
3152
|
+
s.id
|
|
3153
|
+
)) }),
|
|
3154
|
+
current && /* @__PURE__ */ jsx(
|
|
3155
|
+
"div",
|
|
3156
|
+
{
|
|
3157
|
+
className: "caos-wb-rail-panel",
|
|
3158
|
+
role: "tabpanel",
|
|
3159
|
+
id: `${baseId}-rp-${current.id}`,
|
|
3160
|
+
"aria-labelledby": `${baseId}-rs-${current.id}`,
|
|
3161
|
+
children: current.content
|
|
3162
|
+
}
|
|
3163
|
+
)
|
|
3164
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-panel", children: rail })
|
|
3165
|
+
] });
|
|
3166
|
+
}
|
|
3167
|
+
function WorkbenchLayout({ rail, railHead, children, railLabel, className }) {
|
|
3168
|
+
const lang = useShellLang();
|
|
3169
|
+
const label = pick(railLabel ?? { en: "Controls", es: "Controles" }, lang);
|
|
2810
3170
|
return /* @__PURE__ */ jsxs("div", { className: ["page-body", "wide", "caos-wb", className].filter(Boolean).join(" "), "data-workbench": "", children: [
|
|
2811
|
-
/* @__PURE__ */
|
|
2812
|
-
railHead && /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-head", children: railHead }),
|
|
2813
|
-
sections ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2814
|
-
/* @__PURE__ */ jsx("div", { className: "caos-wb-rail-sections", role: "tablist", "aria-label": label, ref: rowRef, children: sections.map((s, idx) => /* @__PURE__ */ jsx(
|
|
2815
|
-
"button",
|
|
2816
|
-
{
|
|
2817
|
-
type: "button",
|
|
2818
|
-
role: "tab",
|
|
2819
|
-
id: `${baseId}-rs-${s.id}`,
|
|
2820
|
-
"aria-selected": s.id === current?.id,
|
|
2821
|
-
"aria-controls": `${baseId}-rp-${s.id}`,
|
|
2822
|
-
tabIndex: s.id === current?.id ? 0 : -1,
|
|
2823
|
-
className: s.id === current?.id ? "chip on" : "chip",
|
|
2824
|
-
onClick: () => setActive(s.id),
|
|
2825
|
-
onKeyDown: (e) => onKeyDown(e, idx),
|
|
2826
|
-
children: pick(s.label, lang)
|
|
2827
|
-
},
|
|
2828
|
-
s.id
|
|
2829
|
-
)) }),
|
|
2830
|
-
current && /* @__PURE__ */ jsx(
|
|
2831
|
-
"div",
|
|
2832
|
-
{
|
|
2833
|
-
className: "caos-wb-rail-panel",
|
|
2834
|
-
role: "tabpanel",
|
|
2835
|
-
id: `${baseId}-rp-${current.id}`,
|
|
2836
|
-
"aria-labelledby": `${baseId}-rs-${current.id}`,
|
|
2837
|
-
children: current.content
|
|
2838
|
-
}
|
|
2839
|
-
)
|
|
2840
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "caos-wb-rail-panel", children: rail })
|
|
2841
|
-
] }),
|
|
3171
|
+
/* @__PURE__ */ jsx("aside", { className: "caos-wb-rail", "aria-label": label, "data-rail": "", children: /* @__PURE__ */ jsx(RailBody, { rail, railHead, railLabel }) }),
|
|
2842
3172
|
/* @__PURE__ */ jsx("section", { className: "caos-wb-main", "data-instrument": "", children })
|
|
2843
3173
|
] });
|
|
2844
3174
|
}
|
|
@@ -2913,13 +3243,73 @@ function VariantBar({ variants, activeId, onSelect, title, lane }) {
|
|
|
2913
3243
|
active?.note && /* @__PURE__ */ jsx("p", { className: "caos-variant-note", children: pick(active.note, lang) })
|
|
2914
3244
|
] });
|
|
2915
3245
|
}
|
|
3246
|
+
|
|
3247
|
+
// src/workbench/focus.ts
|
|
3248
|
+
var FOCUS_TEXT = {
|
|
3249
|
+
enter: { en: "Focus", es: "Enfocar" },
|
|
3250
|
+
enterLabel: { en: "Open this case in focus mode", es: "Abrir este caso en modo enfoque" },
|
|
3251
|
+
exit: { en: "Back to the App", es: "Volver a la App" },
|
|
3252
|
+
stage: { en: "Focus stage", es: "Escenario de enfoque" }
|
|
3253
|
+
};
|
|
3254
|
+
var FOCUS_PARAM = "focus";
|
|
2916
3255
|
var MAX_WORKBENCH_GROUPS = MAX_PEER_TABS;
|
|
2917
3256
|
function panel(id, lane, provenance, content) {
|
|
2918
3257
|
return /* @__PURE__ */ jsx("div", { className: "caos-cw-panel", "data-group": id, "data-lane": lane, "data-provenance": provenance, children: content });
|
|
2919
3258
|
}
|
|
2920
3259
|
function CaseWorkbench(props) {
|
|
2921
|
-
const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel } = props;
|
|
3260
|
+
const { caseId, cases, source, controls, replayOnly, variants, rail, railLabel, groups, compare, context: context2, group, onGroupChange, deepLinkView = true, ariaLabel, focus } = props;
|
|
2922
3261
|
const lang = useShellLang();
|
|
3262
|
+
const focusCfg = focus === true ? {} : focus || null;
|
|
3263
|
+
const focusParam = focusCfg?.param ?? FOCUS_PARAM;
|
|
3264
|
+
const [focusOn, setFocusOn] = useState(() => focusCfg !== null && typeof window !== "undefined" && readCaseParam(window.location.search, focusParam) === "1");
|
|
3265
|
+
const enterRef = useRef(null);
|
|
3266
|
+
const memory = useRef(/* @__PURE__ */ new Map());
|
|
3267
|
+
const turned = useRef(null);
|
|
3268
|
+
const onChangeRef = useRef(focusCfg?.onChange);
|
|
3269
|
+
onChangeRef.current = focusCfg?.onChange;
|
|
3270
|
+
const setFocus = useCallback(
|
|
3271
|
+
(on) => {
|
|
3272
|
+
turned.current = on ? "in" : "out";
|
|
3273
|
+
setFocusOn(on);
|
|
3274
|
+
if (on) replaceQueryParam(focusParam, "1");
|
|
3275
|
+
else removeQueryParam(focusParam);
|
|
3276
|
+
onChangeRef.current?.(on);
|
|
3277
|
+
},
|
|
3278
|
+
[focusParam]
|
|
3279
|
+
);
|
|
3280
|
+
const showFocus = focusCfg !== null && focusOn;
|
|
3281
|
+
useEffect(() => {
|
|
3282
|
+
if (!showFocus) return void 0;
|
|
3283
|
+
const onKey = (e) => {
|
|
3284
|
+
if (e.key !== "Escape" || e.defaultPrevented) return;
|
|
3285
|
+
e.preventDefault();
|
|
3286
|
+
setFocus(false);
|
|
3287
|
+
};
|
|
3288
|
+
document.addEventListener("keydown", onKey);
|
|
3289
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
3290
|
+
}, [showFocus, setFocus]);
|
|
3291
|
+
useEffect(() => {
|
|
3292
|
+
if (!showFocus) return void 0;
|
|
3293
|
+
const frame = document.querySelector("[data-focus-shell]");
|
|
3294
|
+
if (!frame) return void 0;
|
|
3295
|
+
const made = [];
|
|
3296
|
+
for (let node = frame; node && node !== document.body; node = node.parentElement) {
|
|
3297
|
+
for (const sib of Array.from(node.parentElement?.children ?? [])) {
|
|
3298
|
+
if (sib !== node && !sib.hasAttribute("inert") && sib.tagName !== "SCRIPT" && sib.tagName !== "STYLE") {
|
|
3299
|
+
sib.setAttribute("inert", "");
|
|
3300
|
+
made.push(sib);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
return () => {
|
|
3305
|
+
for (const el of made) el.removeAttribute("inert");
|
|
3306
|
+
};
|
|
3307
|
+
}, [showFocus]);
|
|
3308
|
+
useEffect(() => {
|
|
3309
|
+
if (turned.current === "in" && showFocus) document.querySelector("[data-focus-exit]")?.focus();
|
|
3310
|
+
else if (turned.current === "out" && !showFocus) enterRef.current?.focus();
|
|
3311
|
+
turned.current = null;
|
|
3312
|
+
}, [showFocus]);
|
|
2923
3313
|
const variantId = variants?.activeId;
|
|
2924
3314
|
const stateKey = useMemo(
|
|
2925
3315
|
() => makeStateKey({ caseId, variantId: variantId ?? null, source: source ?? null, controls: controls ?? {} }),
|
|
@@ -2944,32 +3334,67 @@ function CaseWorkbench(props) {
|
|
|
2944
3334
|
);
|
|
2945
3335
|
}
|
|
2946
3336
|
const [openGroup, selectGroup] = useUrlView(tabs.map((t2) => t2.id), viewParamOf(deepLinkView), group, onGroupChange);
|
|
2947
|
-
const
|
|
2948
|
-
|
|
3337
|
+
const enter = focusCfg && /* @__PURE__ */ jsxs(
|
|
3338
|
+
"button",
|
|
3339
|
+
{
|
|
3340
|
+
ref: enterRef,
|
|
3341
|
+
type: "button",
|
|
3342
|
+
className: "btn caos-focus-enter",
|
|
3343
|
+
"data-focus-enter": "",
|
|
3344
|
+
"aria-label": FOCUS_TEXT.enterLabel[lang],
|
|
3345
|
+
title: FOCUS_TEXT.enterLabel[lang],
|
|
3346
|
+
onClick: () => setFocus(true),
|
|
3347
|
+
children: [
|
|
3348
|
+
/* @__PURE__ */ jsx(Maximize2, { size: 14, "aria-hidden": "true" }),
|
|
3349
|
+
/* @__PURE__ */ jsx("span", { children: FOCUS_TEXT.enter[lang] })
|
|
3350
|
+
]
|
|
3351
|
+
}
|
|
3352
|
+
);
|
|
3353
|
+
const besideSelect = Boolean(cases && (cases.layout ?? "chips") === "select");
|
|
3354
|
+
const pickers = cases || variants ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3355
|
+
cases && /* @__PURE__ */ jsx(CaseSelector, { lang, ...cases, accessory: besideSelect ? enter : cases.accessory }),
|
|
2949
3356
|
variants && /* @__PURE__ */ jsx(VariantBar, { ...variants })
|
|
2950
3357
|
] }) : null;
|
|
2951
|
-
|
|
2952
|
-
|
|
3358
|
+
const railHead = pickers || enter ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3359
|
+
pickers,
|
|
3360
|
+
besideSelect ? null : enter
|
|
3361
|
+
] }) : null;
|
|
3362
|
+
const instrumentData = {
|
|
3363
|
+
"data-case-workbench": "",
|
|
3364
|
+
"data-group-count": tabs.length,
|
|
3365
|
+
"data-case": caseId,
|
|
3366
|
+
"data-variant": variantId ?? "",
|
|
3367
|
+
"data-source": source ?? "",
|
|
3368
|
+
"data-state-key": stateKey,
|
|
3369
|
+
"data-replay-only": replayOnly ? "1" : void 0
|
|
3370
|
+
};
|
|
3371
|
+
if (showFocus && focusCfg) {
|
|
3372
|
+
const open = tabs.find((t2) => t2.id === openGroup) ?? tabs[0];
|
|
3373
|
+
const caseName = cases?.cases.find((c) => c.id === caseId)?.name ?? caseId;
|
|
3374
|
+
return /* @__PURE__ */ jsx(ViewMemoryContext.Provider, { value: memory.current, children: /* @__PURE__ */ jsx(WorkbenchStateContext.Provider, { value: { stateKey, caseId, variantId, source }, children: /* @__PURE__ */ jsx(
|
|
3375
|
+
FocusShell,
|
|
3376
|
+
{
|
|
3377
|
+
title: focusCfg.title !== void 0 ? pick(focusCfg.title, lang) : caseName,
|
|
3378
|
+
description: focusCfg.description !== void 0 ? pick(focusCfg.description, lang) : open?.label ?? "",
|
|
3379
|
+
hud: (focusCfg.hud ?? []).map((h) => ({ label: pick(h.label, lang), value: h.value })),
|
|
3380
|
+
onExit: () => setFocus(false),
|
|
3381
|
+
exitLabel: FOCUS_TEXT.exit[lang],
|
|
3382
|
+
stageLabel: FOCUS_TEXT.stage[lang],
|
|
3383
|
+
landmark: false,
|
|
3384
|
+
stage: /* @__PURE__ */ jsx("div", { className: "caos-cw caos-cw-focus", ...instrumentData, "data-focus-group": open?.id, "data-instrument": "", children: /* @__PURE__ */ jsx("div", { className: "tabs", children: open && /* @__PURE__ */ jsx("div", { className: "tabpanel", role: "tabpanel", tabIndex: 0, "aria-label": open.label, "data-panel": open.id, children: /* @__PURE__ */ jsx(PanelBoundary, { panel: open.id, children: /* @__PURE__ */ jsx(DocRefsScope, { children: open.content }) }) }) }) }),
|
|
3385
|
+
rail: /* @__PURE__ */ jsx("div", { className: "caos-focus-rail-body", children: /* @__PURE__ */ jsx(RailBody, { rail: rail ?? null, railHead: pickers, railLabel }) })
|
|
3386
|
+
}
|
|
3387
|
+
) }) });
|
|
3388
|
+
}
|
|
3389
|
+
return /* @__PURE__ */ jsx(ViewMemoryContext.Provider, { value: memory.current, children: /* @__PURE__ */ jsx(WorkbenchStateContext.Provider, { value: { stateKey, caseId, variantId, source }, children: /* @__PURE__ */ jsx(WorkbenchLayout, { rail: rail ?? null, railHead, railLabel, children: /* @__PURE__ */ jsx("div", { className: "caos-cw", ...instrumentData, children: /* @__PURE__ */ jsx(
|
|
3390
|
+
Tabs,
|
|
2953
3391
|
{
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
"
|
|
2958
|
-
"data-variant": variantId ?? "",
|
|
2959
|
-
"data-source": source ?? "",
|
|
2960
|
-
"data-state-key": stateKey,
|
|
2961
|
-
"data-replay-only": replayOnly ? "1" : void 0,
|
|
2962
|
-
children: /* @__PURE__ */ jsx(
|
|
2963
|
-
Tabs,
|
|
2964
|
-
{
|
|
2965
|
-
tabs,
|
|
2966
|
-
value: openGroup,
|
|
2967
|
-
onChange: selectGroup,
|
|
2968
|
-
ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang)
|
|
2969
|
-
}
|
|
2970
|
-
)
|
|
3392
|
+
tabs,
|
|
3393
|
+
value: openGroup,
|
|
3394
|
+
onChange: selectGroup,
|
|
3395
|
+
ariaLabel: pick(ariaLabel ?? { en: "Case views", es: "Vistas del caso" }, lang)
|
|
2971
3396
|
}
|
|
2972
|
-
) }) });
|
|
3397
|
+
) }) }) }) });
|
|
2973
3398
|
}
|
|
2974
3399
|
function toneOf(it) {
|
|
2975
3400
|
if (it.tone) return it.tone;
|
|
@@ -3171,4 +3596,4 @@ function ViewsRow({ children, shares }) {
|
|
|
3171
3596
|
}) });
|
|
3172
3597
|
}
|
|
3173
3598
|
|
|
3174
|
-
export { AppShell, ArchitectureModal, BREAKPOINTS, BarChart, Callout, CaseSelector, CaseWorkbench, ChipGroup, CitationsProvider, Cite, DOC_AREAS, DocPage, DocReferences, DocRefsScope, DocSection, EXECUTIVE_SCHEMA_VERSION, Equation, ExecutiveView, FOUR_CS, Figure, FocusShell, Gauge, Glossary, InlineMath, Knob, Landing, LaneBadge, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, PlotCard, Question, Readout, ReferenceList, Refs, Stat, SubTabs, SurfacePage, TabGroups, Tabs, ThemeToggle, VariantBar, Verdict, ViewsRow, WorkbenchLayout, WorkbenchShell, WorkbenchStateContext, caseKind, caseTooltip, casesInSource, createVizLoop, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, readCaseParam, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useUrlView, useWorkbenchState, validateArchitectureConfig, validateExecutive, viewParamOf, widestLabel, withCaseParam };
|
|
3599
|
+
export { AppShell, ArchitectureModal, BREAKPOINTS, BarChart, Callout, CardDeck, CaseSelector, CaseWorkbench, ChipGroup, CitationsProvider, Cite, DECK_TEXT, DOC_AREAS, DocPage, DocReferences, DocRefsScope, DocSection, EXECUTIVE_SCHEMA_VERSION, Equation, ExecutiveView, FLIP_LIMITS, FLIP_TEXT, FOCUS_PARAM, FOCUS_TEXT, FOUR_CS, Figure, FlipCard, FocusShell, Gauge, Glossary, InlineMath, Knob, Landing, LaneBadge, LanguageToggle, MAX_PEER_TABS, MAX_WORKBENCH_GROUPS, PanelBoundary, PlotCard, Question, Readout, ReferenceList, Refs, Stat, SubTabs, SurfacePage, TabGroups, Tabs, ThemeToggle, VariantBar, Verdict, ViewsRow, WorkbenchLayout, WorkbenchShell, WorkbenchStateContext, caseKind, caseTooltip, casesInSource, createVizLoop, deckPosition, deckWindow, firstAppearance, fitLabel, fontFamily, groupByCategory, isStale, makeStateKey, niceStep, niceTicks, perPageFor, readCaseParam, removeQueryParam, replaceQueryParam, sourcesPresent, textWidth, usePausedViz, useUrlView, useWorkbenchState, validateArchitectureConfig, validateExecutive, viewParamOf, widestLabel, withCaseParam };
|