@flytedan/flytebot-design-system 0.28.0 → 0.30.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/dist/index.cjs +910 -761
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -51
- package/dist/index.d.ts +79 -51
- package/dist/index.js +910 -761
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/components.css +50 -0
package/dist/index.js
CHANGED
|
@@ -199,6 +199,15 @@ var ROOT_LAYER = 0;
|
|
|
199
199
|
var LAYER_STEP = 10;
|
|
200
200
|
var ROOT_STEP = 100;
|
|
201
201
|
var OVERLAY_CEILING = 1e4;
|
|
202
|
+
var openEscapeIds = [];
|
|
203
|
+
var registerEscapeLayer = (id) => {
|
|
204
|
+
if (openEscapeIds.includes(id)) return;
|
|
205
|
+
openEscapeIds = [...openEscapeIds, id];
|
|
206
|
+
};
|
|
207
|
+
var unregisterEscapeLayer = (id) => {
|
|
208
|
+
if (!openEscapeIds.includes(id)) return;
|
|
209
|
+
openEscapeIds = openEscapeIds.filter((existing) => existing !== id);
|
|
210
|
+
};
|
|
202
211
|
var LayerContext = React2.createContext(ROOT_LAYER);
|
|
203
212
|
var openRootIds = [];
|
|
204
213
|
var rootListeners = /* @__PURE__ */ new Set();
|
|
@@ -276,6 +285,12 @@ function Popover({
|
|
|
276
285
|
});
|
|
277
286
|
const restore = React2.useRef(null);
|
|
278
287
|
const zIndex = useOverlayLayer(!!open);
|
|
288
|
+
const escapeId = React2.useId();
|
|
289
|
+
React2.useLayoutEffect(() => {
|
|
290
|
+
if (!open) return;
|
|
291
|
+
registerEscapeLayer(escapeId);
|
|
292
|
+
return () => unregisterEscapeLayer(escapeId);
|
|
293
|
+
}, [open, escapeId]);
|
|
279
294
|
React2.useEffect(() => {
|
|
280
295
|
if (!open) return;
|
|
281
296
|
restore.current = typeof document !== "undefined" ? document.activeElement : null;
|
|
@@ -300,10 +315,10 @@ function Popover({
|
|
|
300
315
|
onClose && onClose();
|
|
301
316
|
};
|
|
302
317
|
const key = (e) => {
|
|
303
|
-
if (closeOnEscape
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
318
|
+
if (!closeOnEscape || e.key !== "Escape") return;
|
|
319
|
+
if (openEscapeIds[openEscapeIds.length - 1] !== escapeId) return;
|
|
320
|
+
e.stopPropagation();
|
|
321
|
+
onClose && onClose();
|
|
307
322
|
};
|
|
308
323
|
document.addEventListener("pointerdown", away, true);
|
|
309
324
|
document.addEventListener("keydown", key);
|
|
@@ -311,7 +326,7 @@ function Popover({
|
|
|
311
326
|
document.removeEventListener("pointerdown", away, true);
|
|
312
327
|
document.removeEventListener("keydown", key);
|
|
313
328
|
};
|
|
314
|
-
}, [open, closeOnOutside, closeOnEscape, onClose, anchorRef, triggerRef]);
|
|
329
|
+
}, [open, closeOnOutside, closeOnEscape, onClose, anchorRef, triggerRef, escapeId]);
|
|
315
330
|
if (!open || !pos) return null;
|
|
316
331
|
return portal(
|
|
317
332
|
/* @__PURE__ */ jsx3(LayerProvider, { zIndex, children: /* @__PURE__ */ jsxs3(
|
|
@@ -1500,11 +1515,13 @@ var BANNER_TONE_STYLE = {
|
|
|
1500
1515
|
text: "var(--danger-text)"
|
|
1501
1516
|
}
|
|
1502
1517
|
};
|
|
1503
|
-
function Banner({ tone = "info", title, detail, actions }) {
|
|
1518
|
+
function Banner({ tone = "info", title, detail, actions, role }) {
|
|
1504
1519
|
const t = BANNER_TONE_STYLE[tone];
|
|
1520
|
+
const liveRole = role || (tone === "danger" ? "alert" : "status");
|
|
1505
1521
|
return /* @__PURE__ */ jsxs19(
|
|
1506
1522
|
"div",
|
|
1507
1523
|
{
|
|
1524
|
+
role: liveRole,
|
|
1508
1525
|
style: {
|
|
1509
1526
|
display: "flex",
|
|
1510
1527
|
alignItems: "flex-start",
|
|
@@ -2714,7 +2731,7 @@ function DataTable({
|
|
|
2714
2731
|
}
|
|
2715
2732
|
|
|
2716
2733
|
// src/components/data/FilterBar.tsx
|
|
2717
|
-
import * as
|
|
2734
|
+
import * as React24 from "react";
|
|
2718
2735
|
|
|
2719
2736
|
// src/components/forms/NumberInput.tsx
|
|
2720
2737
|
import * as React21 from "react";
|
|
@@ -2762,21 +2779,36 @@ function NumberInput({
|
|
|
2762
2779
|
"aria-label": ariaLabel
|
|
2763
2780
|
}) {
|
|
2764
2781
|
const fieldId = useFieldId(id);
|
|
2782
|
+
const errorId = fieldId + "-error";
|
|
2765
2783
|
const num = (v) => {
|
|
2766
|
-
const
|
|
2784
|
+
const s = String(v == null ? "" : v).replace(/[^0-9.-]/g, "");
|
|
2785
|
+
if (s === "" || s === "-") return null;
|
|
2786
|
+
const n = Number(s);
|
|
2767
2787
|
return isNaN(n) ? null : n;
|
|
2768
2788
|
};
|
|
2789
|
+
const requiresInteger = Number.isInteger(step);
|
|
2790
|
+
const validationMessage = (n) => {
|
|
2791
|
+
if (requiresInteger && !Number.isInteger(n)) return "Enter a whole number";
|
|
2792
|
+
if (n < min) return "Must be \u2265 " + min;
|
|
2793
|
+
if (n > max) return "Must be \u2264 " + max;
|
|
2794
|
+
return null;
|
|
2795
|
+
};
|
|
2769
2796
|
const [text, setText] = React21.useState(value == null || value === "" ? "" : String(value));
|
|
2770
2797
|
const [editing, setEditing] = React21.useState(false);
|
|
2798
|
+
const [invalidMessage, setInvalidMessage] = React21.useState(null);
|
|
2771
2799
|
const timer = React21.useRef(null);
|
|
2772
2800
|
React21.useEffect(() => {
|
|
2773
|
-
if (!editing)
|
|
2801
|
+
if (!editing) {
|
|
2802
|
+
setText(value == null || value === "" ? "" : String(value));
|
|
2803
|
+
setInvalidMessage(null);
|
|
2804
|
+
}
|
|
2774
2805
|
}, [value, editing]);
|
|
2775
2806
|
const clamp = (n) => Math.min(max, Math.max(min, n));
|
|
2776
2807
|
const fire = (n) => onChange && onChange({ target: { value: n } });
|
|
2777
2808
|
const bump = (dir, mult = 1) => {
|
|
2778
2809
|
const cur = num(text) ?? num(value) ?? (min === -Infinity ? 0 : min);
|
|
2779
2810
|
const next = clamp(cur + dir * (bigStep && mult > 1 ? bigStep : step) * (bigStep ? 1 : mult));
|
|
2811
|
+
setInvalidMessage(null);
|
|
2780
2812
|
setText(String(next));
|
|
2781
2813
|
fire(next);
|
|
2782
2814
|
};
|
|
@@ -2800,7 +2832,8 @@ function NumberInput({
|
|
|
2800
2832
|
const n = num(text);
|
|
2801
2833
|
return n == null ? "" : format ? n.toLocaleString() : String(n);
|
|
2802
2834
|
})();
|
|
2803
|
-
const
|
|
2835
|
+
const shownError = error || invalidMessage || void 0;
|
|
2836
|
+
const box = ["fd-input", "fd-input-num", shownError ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
|
|
2804
2837
|
const fieldName = label || ariaLabel;
|
|
2805
2838
|
const stepperName = (verb) => fieldName ? verb + " " + fieldName : verb;
|
|
2806
2839
|
return /* @__PURE__ */ jsxs36("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
|
|
@@ -2816,20 +2849,29 @@ function NumberInput({
|
|
|
2816
2849
|
disabled,
|
|
2817
2850
|
placeholder,
|
|
2818
2851
|
value: shown,
|
|
2852
|
+
"aria-invalid": shownError ? "true" : void 0,
|
|
2853
|
+
"aria-describedby": shownError ? errorId : void 0,
|
|
2819
2854
|
onFocus: () => setEditing(true),
|
|
2820
2855
|
onBlur: () => {
|
|
2821
2856
|
setEditing(false);
|
|
2822
|
-
|
|
2823
|
-
if (n != null) {
|
|
2824
|
-
const c = clamp(n);
|
|
2825
|
-
setText(String(c));
|
|
2826
|
-
fire(c);
|
|
2827
|
-
}
|
|
2857
|
+
if (text.trim() === "") fire(void 0);
|
|
2828
2858
|
},
|
|
2829
2859
|
onChange: (e) => {
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
if (
|
|
2860
|
+
const raw = e.target.value;
|
|
2861
|
+
setText(raw);
|
|
2862
|
+
if (raw.trim() === "") {
|
|
2863
|
+
setInvalidMessage(null);
|
|
2864
|
+
fire(void 0);
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
const n = num(raw);
|
|
2868
|
+
if (n == null) {
|
|
2869
|
+
setInvalidMessage("Enter a number");
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
const message = validationMessage(n);
|
|
2873
|
+
setInvalidMessage(message);
|
|
2874
|
+
if (!message) fire(n);
|
|
2833
2875
|
},
|
|
2834
2876
|
onKeyDown: (e) => {
|
|
2835
2877
|
if (e.key === "ArrowUp") {
|
|
@@ -2872,9 +2914,9 @@ function NumberInput({
|
|
|
2872
2914
|
)
|
|
2873
2915
|
] })
|
|
2874
2916
|
] }),
|
|
2875
|
-
|
|
2917
|
+
shownError ? /* @__PURE__ */ jsxs36("span", { id: errorId, className: "fd-field-error", children: [
|
|
2876
2918
|
/* @__PURE__ */ jsx42("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
|
|
2877
|
-
|
|
2919
|
+
shownError
|
|
2878
2920
|
] }) : help ? /* @__PURE__ */ jsx42("span", { className: "fd-field-help", children: help }) : null
|
|
2879
2921
|
] });
|
|
2880
2922
|
}
|
|
@@ -3096,16 +3138,48 @@ function Textarea({ label, help, error, required = false, rows = 4, disabled = f
|
|
|
3096
3138
|
] });
|
|
3097
3139
|
}
|
|
3098
3140
|
|
|
3141
|
+
// src/components/forms/listbox.ts
|
|
3142
|
+
import * as React23 from "react";
|
|
3143
|
+
function useListboxNav({ count, onSelect }) {
|
|
3144
|
+
const [active, setActive] = React23.useState(-1);
|
|
3145
|
+
return {
|
|
3146
|
+
active,
|
|
3147
|
+
setActive,
|
|
3148
|
+
move: (dir) => setActive((a) => Math.min(count - 1, Math.max(0, a + dir))),
|
|
3149
|
+
first: () => setActive(0),
|
|
3150
|
+
last: () => setActive(count - 1),
|
|
3151
|
+
activate: () => {
|
|
3152
|
+
if (active >= 0 && active < count) onSelect(active);
|
|
3153
|
+
}
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
function useListboxScrollIntoView(active, listRef, enabled) {
|
|
3157
|
+
React23.useEffect(() => {
|
|
3158
|
+
if (!enabled || active < 0 || !listRef.current) return;
|
|
3159
|
+
const list = listRef.current;
|
|
3160
|
+
const el = list.querySelector('[data-i="' + active + '"]');
|
|
3161
|
+
if (!el) return;
|
|
3162
|
+
const top = el.offsetTop - list.offsetTop;
|
|
3163
|
+
if (top < list.scrollTop) list.scrollTop = top - 4;
|
|
3164
|
+
else if (top + el.offsetHeight > list.scrollTop + list.clientHeight) {
|
|
3165
|
+
list.scrollTop = top + el.offsetHeight - list.clientHeight + 4;
|
|
3166
|
+
}
|
|
3167
|
+
}, [active, enabled]);
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3099
3170
|
// src/components/data/FilterBar.tsx
|
|
3100
3171
|
import { jsx as jsx45, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
3172
|
+
var PANEL_WIDTH = { multiSelect: 280, range: 240, date: 300 };
|
|
3101
3173
|
var isSet = (v) => !(v == null || v === "" || v === false);
|
|
3174
|
+
function operatorsFor(field) {
|
|
3175
|
+
return field.operators && field.operators.length ? field.operators : ["in", "notIn"];
|
|
3176
|
+
}
|
|
3102
3177
|
function isFieldSet(field, value) {
|
|
3103
3178
|
switch (field.type) {
|
|
3104
3179
|
case "text":
|
|
3105
3180
|
case "select":
|
|
3106
3181
|
return isSet(value);
|
|
3107
|
-
case "multiSelect":
|
|
3108
|
-
case "pasteList": {
|
|
3182
|
+
case "multiSelect": {
|
|
3109
3183
|
const v = value;
|
|
3110
3184
|
return !!v && Array.isArray(v.values) && v.values.length > 0;
|
|
3111
3185
|
}
|
|
@@ -3116,7 +3190,7 @@ function isFieldSet(field, value) {
|
|
|
3116
3190
|
case "date": {
|
|
3117
3191
|
const v = value;
|
|
3118
3192
|
if (!v) return false;
|
|
3119
|
-
return v.kind === "withinDays" ? v.days != null : !!(v.from
|
|
3193
|
+
return v.kind === "withinDays" ? v.days != null : !!(v.from && v.to);
|
|
3120
3194
|
}
|
|
3121
3195
|
}
|
|
3122
3196
|
}
|
|
@@ -3128,8 +3202,7 @@ function chipLabel(field, value) {
|
|
|
3128
3202
|
const chosen = field.options.find((o) => o.value === value);
|
|
3129
3203
|
return chosen ? chosen.label : String(value ?? "");
|
|
3130
3204
|
}
|
|
3131
|
-
case "multiSelect":
|
|
3132
|
-
case "pasteList": {
|
|
3205
|
+
case "multiSelect": {
|
|
3133
3206
|
const v = value;
|
|
3134
3207
|
if (!v || !v.values.length) return "";
|
|
3135
3208
|
const prefix = v.operator === "notIn" ? "Not " : "";
|
|
@@ -3147,7 +3220,7 @@ function chipLabel(field, value) {
|
|
|
3147
3220
|
const v = value;
|
|
3148
3221
|
if (!v) return "";
|
|
3149
3222
|
if (v.kind === "withinDays") return "Last " + v.days + " day" + (v.days === 1 ? "" : "s");
|
|
3150
|
-
return
|
|
3223
|
+
return v.from + " \u2192 " + v.to;
|
|
3151
3224
|
}
|
|
3152
3225
|
}
|
|
3153
3226
|
}
|
|
@@ -3169,338 +3242,395 @@ function OperatorToggle({
|
|
|
3169
3242
|
}
|
|
3170
3243
|
);
|
|
3171
3244
|
}
|
|
3172
|
-
function EditPopover({ zIndex, width, children }) {
|
|
3173
|
-
return /* @__PURE__ */ jsx45(LayerProvider, { zIndex, children: /* @__PURE__ */ jsx45(
|
|
3174
|
-
"span",
|
|
3175
|
-
{
|
|
3176
|
-
className: "fd-view-enter",
|
|
3177
|
-
style: {
|
|
3178
|
-
position: "absolute",
|
|
3179
|
-
top: "calc(100% + 6px)",
|
|
3180
|
-
left: 0,
|
|
3181
|
-
zIndex,
|
|
3182
|
-
width,
|
|
3183
|
-
maxWidth: "calc(100vw - 32px)",
|
|
3184
|
-
display: "block",
|
|
3185
|
-
background: "var(--surface)",
|
|
3186
|
-
border: "1px solid var(--border)",
|
|
3187
|
-
borderRadius: 10,
|
|
3188
|
-
boxShadow: "0 16px 44px rgba(11,13,17,.18)",
|
|
3189
|
-
padding: "var(--s-8)"
|
|
3190
|
-
},
|
|
3191
|
-
children
|
|
3192
|
-
}
|
|
3193
|
-
) });
|
|
3194
|
-
}
|
|
3195
3245
|
function MultiSelectPanel({
|
|
3196
3246
|
field,
|
|
3197
3247
|
value,
|
|
3248
|
+
anchorRef,
|
|
3198
3249
|
onChange,
|
|
3250
|
+
onRemove,
|
|
3199
3251
|
onClose
|
|
3200
3252
|
}) {
|
|
3201
|
-
const
|
|
3253
|
+
const allowedOps = operatorsFor(field);
|
|
3254
|
+
const [draftOperator, setDraftOperator] = React24.useState(value?.operator ?? allowedOps[0]);
|
|
3255
|
+
const operator = allowedOps.length === 1 ? allowedOps[0] : value?.operator ?? draftOperator;
|
|
3202
3256
|
const selected = value?.values ?? [];
|
|
3203
|
-
const
|
|
3204
|
-
|
|
3205
|
-
const
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3257
|
+
const selectedRef = React24.useRef(selected);
|
|
3258
|
+
selectedRef.current = selected;
|
|
3259
|
+
const commit = (nextValues, nextOperator = operator) => {
|
|
3260
|
+
if (nextValues.length === 0) onRemove();
|
|
3261
|
+
else onChange({ operator: nextOperator, values: nextValues });
|
|
3262
|
+
};
|
|
3263
|
+
const setOperator = (op) => {
|
|
3264
|
+
setDraftOperator(op);
|
|
3265
|
+
if (selected.length > 0) commit(selected, op);
|
|
3266
|
+
};
|
|
3267
|
+
const [mode2, setMode] = React24.useState("search");
|
|
3268
|
+
const [query, setQuery] = React24.useState("");
|
|
3269
|
+
const [asyncState, setAsyncState] = React24.useState(
|
|
3270
|
+
{ options: null, loading: !!field.loadOptions, error: false }
|
|
3271
|
+
);
|
|
3272
|
+
const [retryTick, setRetryTick] = React24.useState(0);
|
|
3273
|
+
const inputRef = React24.useRef(null);
|
|
3274
|
+
const listRef = React24.useRef(null);
|
|
3275
|
+
const listId = React24.useId();
|
|
3276
|
+
const loadOptionsRef = React24.useRef(field.loadOptions);
|
|
3277
|
+
loadOptionsRef.current = field.loadOptions;
|
|
3278
|
+
React24.useEffect(() => {
|
|
3210
3279
|
inputRef.current?.focus();
|
|
3211
3280
|
}, []);
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3281
|
+
React24.useEffect(() => {
|
|
3282
|
+
const load = loadOptionsRef.current;
|
|
3283
|
+
if (!load) return;
|
|
3284
|
+
const controller = new AbortController();
|
|
3285
|
+
setAsyncState((s) => ({ options: s.options, loading: true, error: false }));
|
|
3217
3286
|
const t = setTimeout(() => {
|
|
3218
|
-
|
|
3219
|
-
if (
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
if (!cancelled) {
|
|
3225
|
-
setAsyncOptions([]);
|
|
3226
|
-
setLoading(false);
|
|
3227
|
-
}
|
|
3287
|
+
load(query, controller.signal).then((opts) => {
|
|
3288
|
+
if (controller.signal.aborted) return;
|
|
3289
|
+
setAsyncState({ options: opts, loading: false, error: false });
|
|
3290
|
+
}).catch((err) => {
|
|
3291
|
+
if (controller.signal.aborted || err && err.name === "AbortError") return;
|
|
3292
|
+
setAsyncState({ options: null, loading: false, error: true });
|
|
3228
3293
|
});
|
|
3229
3294
|
}, 300);
|
|
3230
3295
|
return () => {
|
|
3231
|
-
cancelled = true;
|
|
3232
3296
|
clearTimeout(t);
|
|
3297
|
+
controller.abort();
|
|
3233
3298
|
};
|
|
3234
|
-
}, [query,
|
|
3235
|
-
const options = field.loadOptions ?
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3299
|
+
}, [query, retryTick]);
|
|
3300
|
+
const options = field.loadOptions ? asyncState.options ?? [] : (field.options ?? []).filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
|
|
3301
|
+
const showLoading = !!field.loadOptions && asyncState.loading;
|
|
3302
|
+
const showError = !!field.loadOptions && asyncState.error;
|
|
3303
|
+
const hasListbox = !showLoading && !showError && options.length > 0;
|
|
3239
3304
|
const isSelected = (o) => selected.some((s) => s.value === o.value);
|
|
3240
3305
|
const toggle = (o) => {
|
|
3241
3306
|
const next = isSelected(o) ? selected.filter((s) => s.value !== o.value) : [...selected, o];
|
|
3242
|
-
|
|
3307
|
+
commit(next);
|
|
3308
|
+
};
|
|
3309
|
+
const nav = useListboxNav({ count: options.length, onSelect: (i) => toggle(options[i]) });
|
|
3310
|
+
useListboxScrollIntoView(nav.active, listRef, hasListbox);
|
|
3311
|
+
React24.useEffect(() => {
|
|
3312
|
+
nav.setActive(0);
|
|
3313
|
+
}, [query, options.length]);
|
|
3314
|
+
const activeId = hasListbox && options[nav.active] ? listId + "-opt-" + nav.active : void 0;
|
|
3315
|
+
const liveRole = showError ? "alert" : "status";
|
|
3316
|
+
const liveMessage = showLoading ? "Loading\u2026" : showError ? "Couldn't load options." : options.length === 0 ? "No matches" : "";
|
|
3317
|
+
const [pasteText, setPasteText] = React24.useState("");
|
|
3318
|
+
const [pasteLoading, setPasteLoading] = React24.useState(false);
|
|
3319
|
+
const [unmatchedTokens, setUnmatchedTokens] = React24.useState([]);
|
|
3320
|
+
const [pasteError, setPasteError] = React24.useState(null);
|
|
3321
|
+
const pasteWrapRef = React24.useRef(null);
|
|
3322
|
+
React24.useEffect(() => {
|
|
3323
|
+
if (mode2 === "paste") pasteWrapRef.current?.querySelector("textarea")?.focus();
|
|
3324
|
+
}, [mode2]);
|
|
3325
|
+
const doResolve = () => {
|
|
3326
|
+
if (!field.resolve) return;
|
|
3327
|
+
const tokens = Array.from(new Set(pasteText.split(/[\n,]/).map((t) => t.trim()).filter(Boolean)));
|
|
3328
|
+
if (!tokens.length) return;
|
|
3329
|
+
setPasteLoading(true);
|
|
3330
|
+
setPasteError(null);
|
|
3331
|
+
field.resolve(tokens).then((result) => {
|
|
3332
|
+
setPasteLoading(false);
|
|
3333
|
+
setUnmatchedTokens(result.unmatched);
|
|
3334
|
+
const merged = new Map(selectedRef.current.map((s) => [s.value, s]));
|
|
3335
|
+
for (const m of result.matched) merged.set(m.value, { value: m.value, label: m.label });
|
|
3336
|
+
commit(Array.from(merged.values()));
|
|
3337
|
+
setPasteText("");
|
|
3338
|
+
}).catch((err) => {
|
|
3339
|
+
setPasteLoading(false);
|
|
3340
|
+
setPasteError(err instanceof Error ? err.message : "Could not resolve the pasted list.");
|
|
3341
|
+
});
|
|
3243
3342
|
};
|
|
3244
|
-
const
|
|
3245
|
-
|
|
3246
|
-
/* @__PURE__ */ jsx45(
|
|
3343
|
+
const header = /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3344
|
+
allowedOps.length > 1 ? /* @__PURE__ */ jsx45(OperatorToggle, { operator, onChange: setOperator, label: field.label }) : null,
|
|
3345
|
+
field.resolve ? /* @__PURE__ */ jsx45(
|
|
3346
|
+
SegmentedControl,
|
|
3347
|
+
{
|
|
3348
|
+
"aria-label": field.label + " \u2014 search or paste a list",
|
|
3349
|
+
options: [{ value: "search", label: "Search" }, { value: "paste", label: "Paste a list" }],
|
|
3350
|
+
value: mode2,
|
|
3351
|
+
onChange: (m) => setMode(m)
|
|
3352
|
+
}
|
|
3353
|
+
) : null,
|
|
3354
|
+
mode2 === "search" ? /* @__PURE__ */ jsxs39("span", { className: "fd-pop-search", children: [
|
|
3355
|
+
/* @__PURE__ */ jsx45("i", { className: "ph ph-magnifying-glass", "aria-hidden": "true" }),
|
|
3356
|
+
/* @__PURE__ */ jsx45(
|
|
3357
|
+
"input",
|
|
3358
|
+
{
|
|
3359
|
+
ref: inputRef,
|
|
3360
|
+
role: "combobox",
|
|
3361
|
+
"aria-expanded": hasListbox,
|
|
3362
|
+
"aria-controls": hasListbox ? listId : void 0,
|
|
3363
|
+
"aria-autocomplete": "list",
|
|
3364
|
+
"aria-activedescendant": activeId,
|
|
3365
|
+
"aria-label": field.label,
|
|
3366
|
+
value: query,
|
|
3367
|
+
placeholder: field.placeholder || "Search\u2026",
|
|
3368
|
+
onChange: (e) => setQuery(e.target.value),
|
|
3369
|
+
onKeyDown: (e) => {
|
|
3370
|
+
if (e.key === "ArrowDown") {
|
|
3371
|
+
e.preventDefault();
|
|
3372
|
+
nav.move(1);
|
|
3373
|
+
} else if (e.key === "ArrowUp") {
|
|
3374
|
+
e.preventDefault();
|
|
3375
|
+
nav.move(-1);
|
|
3376
|
+
} else if (e.key === "Home") {
|
|
3377
|
+
e.preventDefault();
|
|
3378
|
+
nav.first();
|
|
3379
|
+
} else if (e.key === "End") {
|
|
3380
|
+
e.preventDefault();
|
|
3381
|
+
nav.last();
|
|
3382
|
+
} else if (e.key === "Enter") {
|
|
3383
|
+
e.preventDefault();
|
|
3384
|
+
nav.activate();
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
)
|
|
3389
|
+
] }) : null
|
|
3390
|
+
] });
|
|
3391
|
+
const body = mode2 === "paste" ? /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", ref: pasteWrapRef, children: [
|
|
3247
3392
|
/* @__PURE__ */ jsx45(
|
|
3248
|
-
|
|
3393
|
+
Textarea,
|
|
3249
3394
|
{
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
"aria-label": field.label,
|
|
3257
|
-
className: "fd-input",
|
|
3258
|
-
style: { width: "100%", padding: "6px 10px" },
|
|
3259
|
-
value: query,
|
|
3260
|
-
placeholder: field.placeholder || "Search\u2026",
|
|
3261
|
-
onChange: (e) => setQuery(e.target.value),
|
|
3395
|
+
"aria-label": field.label + " \u2014 paste a list",
|
|
3396
|
+
rows: 4,
|
|
3397
|
+
placeholder: field.pastePlaceholder || field.placeholder || "Paste names, one per line or comma-separated",
|
|
3398
|
+
value: pasteText,
|
|
3399
|
+
disabled: pasteLoading,
|
|
3400
|
+
onChange: (e) => setPasteText(e.target.value),
|
|
3262
3401
|
onKeyDown: (e) => {
|
|
3263
|
-
if (e.key === "
|
|
3264
|
-
e.preventDefault();
|
|
3265
|
-
setActive((a) => Math.min(a + 1, options.length - 1));
|
|
3266
|
-
} else if (e.key === "ArrowUp") {
|
|
3267
|
-
e.preventDefault();
|
|
3268
|
-
setActive((a) => Math.max(a - 1, 0));
|
|
3269
|
-
} else if (e.key === "Enter") {
|
|
3402
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
3270
3403
|
e.preventDefault();
|
|
3271
|
-
|
|
3272
|
-
} else if (e.key === "Escape") {
|
|
3273
|
-
e.stopPropagation();
|
|
3274
|
-
onClose();
|
|
3404
|
+
doResolve();
|
|
3275
3405
|
}
|
|
3276
3406
|
}
|
|
3277
3407
|
}
|
|
3278
3408
|
),
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3409
|
+
pasteError ? /* @__PURE__ */ jsx45(Banner, { tone: "danger", title: "Couldn't resolve the list", detail: pasteError }) : null,
|
|
3410
|
+
unmatchedTokens.length > 0 ? /* @__PURE__ */ jsx45(Banner, { tone: "warn", title: unmatchedTokens.length + " didn't match", detail: unmatchedTokens.join(", ") }) : null,
|
|
3411
|
+
selected.length > 0 ? /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-xs", children: [
|
|
3412
|
+
/* @__PURE__ */ jsxs39("span", { className: "fd-body-sm fd-muted", children: [
|
|
3413
|
+
selected.length,
|
|
3414
|
+
" matched"
|
|
3415
|
+
] }),
|
|
3416
|
+
/* @__PURE__ */ jsx45("span", { className: "fd-row fd-wrap fd-gap-xs", children: selected.map((v) => /* @__PURE__ */ jsx45(Tag, { onRemove: () => commit(selected.filter((s) => s.value !== v.value)), children: v.label }, v.value)) })
|
|
3417
|
+
] }) : null
|
|
3418
|
+
] }) : /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3419
|
+
/* @__PURE__ */ jsxs39(
|
|
3283
3420
|
"span",
|
|
3284
3421
|
{
|
|
3285
|
-
|
|
3286
|
-
role:
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
children: options.map((o, i) => {
|
|
3292
|
-
const on = isSelected(o);
|
|
3293
|
-
return /* @__PURE__ */ jsxs39(
|
|
3294
|
-
"button",
|
|
3295
|
-
{
|
|
3296
|
-
type: "button",
|
|
3297
|
-
id: listId + "-opt-" + i,
|
|
3298
|
-
role: "option",
|
|
3299
|
-
"aria-selected": on,
|
|
3300
|
-
className: "fd-row",
|
|
3301
|
-
onMouseEnter: () => setActive(i),
|
|
3302
|
-
onClick: () => toggle(o),
|
|
3303
|
-
style: {
|
|
3304
|
-
gap: "var(--s-8)",
|
|
3305
|
-
padding: "4px 8px",
|
|
3306
|
-
borderRadius: 7,
|
|
3307
|
-
border: 0,
|
|
3308
|
-
width: "100%",
|
|
3309
|
-
textAlign: "left",
|
|
3310
|
-
cursor: "pointer",
|
|
3311
|
-
background: i === active ? "var(--surface-brand)" : "transparent"
|
|
3312
|
-
},
|
|
3313
|
-
children: [
|
|
3314
|
-
/* @__PURE__ */ jsx45("i", { className: on ? "ph ph-check-square" : "ph ph-square", "aria-hidden": "true" }),
|
|
3315
|
-
/* @__PURE__ */ jsx45("span", { className: "fd-body-sm", style: { flex: 1, color: "var(--text)" }, children: o.label })
|
|
3316
|
-
]
|
|
3317
|
-
},
|
|
3318
|
-
o.value
|
|
3319
|
-
);
|
|
3320
|
-
})
|
|
3422
|
+
className: "fd-row fd-gap-xs" + (liveMessage && !showError ? " fd-muted" : ""),
|
|
3423
|
+
role: liveRole,
|
|
3424
|
+
children: [
|
|
3425
|
+
showLoading ? /* @__PURE__ */ jsx45("span", { className: "fd-spinner", "aria-hidden": "true" }) : null,
|
|
3426
|
+
liveMessage ? /* @__PURE__ */ jsx45("span", { className: "fd-body-sm", children: liveMessage }) : null
|
|
3427
|
+
]
|
|
3321
3428
|
}
|
|
3322
3429
|
),
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3430
|
+
showError ? /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", onClick: () => setRetryTick((n) => n + 1), children: "Retry" }) : null,
|
|
3431
|
+
hasListbox ? /* @__PURE__ */ jsx45("span", { id: listId, role: "listbox", "aria-label": field.label, "aria-multiselectable": "true", ref: listRef, className: "fd-pop-list", children: options.map((o, i) => {
|
|
3432
|
+
const on = isSelected(o);
|
|
3433
|
+
return /* @__PURE__ */ jsxs39(
|
|
3434
|
+
"div",
|
|
3435
|
+
{
|
|
3436
|
+
id: listId + "-opt-" + i,
|
|
3437
|
+
"data-i": i,
|
|
3438
|
+
role: "option",
|
|
3439
|
+
tabIndex: -1,
|
|
3440
|
+
"aria-selected": on,
|
|
3441
|
+
className: ["fd-opt", i === nav.active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
3442
|
+
onMouseEnter: () => nav.setActive(i),
|
|
3443
|
+
onClick: () => toggle(o),
|
|
3444
|
+
children: [
|
|
3445
|
+
/* @__PURE__ */ jsx45("i", { className: "fd-filterbar-opt-check ph " + (on ? "ph-check-square is-checked" : "ph-square"), "aria-hidden": "true" }),
|
|
3446
|
+
/* @__PURE__ */ jsx45("span", { className: "fd-opt-label", children: o.label })
|
|
3447
|
+
]
|
|
3448
|
+
},
|
|
3449
|
+
o.value
|
|
3450
|
+
);
|
|
3451
|
+
}) }) : null
|
|
3327
3452
|
] });
|
|
3453
|
+
const footer = mode2 === "paste" ? /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", onClick: doResolve, disabled: pasteLoading || !pasteText.trim(), children: pasteLoading ? "Resolving\u2026" : "Resolve" }) : void 0;
|
|
3454
|
+
return /* @__PURE__ */ jsx45(
|
|
3455
|
+
Popover,
|
|
3456
|
+
{
|
|
3457
|
+
open: true,
|
|
3458
|
+
anchorRef,
|
|
3459
|
+
onClose,
|
|
3460
|
+
placement: "bottom-start",
|
|
3461
|
+
offset: 6,
|
|
3462
|
+
width: PANEL_WIDTH.multiSelect,
|
|
3463
|
+
label: field.label,
|
|
3464
|
+
header,
|
|
3465
|
+
footer,
|
|
3466
|
+
children: body
|
|
3467
|
+
}
|
|
3468
|
+
);
|
|
3328
3469
|
}
|
|
3329
3470
|
function RangePanel({
|
|
3330
3471
|
field,
|
|
3331
3472
|
value,
|
|
3473
|
+
anchorRef,
|
|
3332
3474
|
onChange,
|
|
3475
|
+
onRemove,
|
|
3333
3476
|
onClose
|
|
3334
3477
|
}) {
|
|
3335
|
-
const
|
|
3336
|
-
const
|
|
3337
|
-
const
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3478
|
+
const [draftMin, setDraftMin] = React24.useState(value?.min);
|
|
3479
|
+
const [draftMax, setDraftMax] = React24.useState(value?.max);
|
|
3480
|
+
const invalid = draftMin != null && draftMax != null && draftMin > draftMax;
|
|
3481
|
+
const apply = (nextMin, nextMax) => {
|
|
3482
|
+
setDraftMin(nextMin);
|
|
3483
|
+
setDraftMax(nextMax);
|
|
3484
|
+
if (nextMin == null && nextMax == null) {
|
|
3485
|
+
onRemove();
|
|
3486
|
+
return;
|
|
3487
|
+
}
|
|
3488
|
+
if (nextMin != null && nextMax != null && nextMin > nextMax) return;
|
|
3489
|
+
onChange({ min: nextMin, max: nextMax });
|
|
3490
|
+
};
|
|
3491
|
+
return /* @__PURE__ */ jsx45(
|
|
3492
|
+
Popover,
|
|
3493
|
+
{
|
|
3494
|
+
open: true,
|
|
3495
|
+
anchorRef,
|
|
3496
|
+
onClose,
|
|
3497
|
+
placement: "bottom-start",
|
|
3498
|
+
offset: 6,
|
|
3499
|
+
width: PANEL_WIDTH.range,
|
|
3500
|
+
label: field.label,
|
|
3501
|
+
footer: /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", onClick: onClose, disabled: invalid, children: "Done" }),
|
|
3502
|
+
children: /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3503
|
+
/* @__PURE__ */ jsx45(
|
|
3504
|
+
NumberInput,
|
|
3505
|
+
{
|
|
3506
|
+
label: field.minLabel || "At least",
|
|
3507
|
+
value: draftMin ?? "",
|
|
3508
|
+
min: field.min,
|
|
3509
|
+
max: field.max,
|
|
3510
|
+
step: field.step,
|
|
3511
|
+
placeholder: field.minPlaceholder,
|
|
3512
|
+
onChange: (e) => apply(e.target.value, draftMax)
|
|
3513
|
+
}
|
|
3514
|
+
),
|
|
3515
|
+
/* @__PURE__ */ jsx45(
|
|
3516
|
+
NumberInput,
|
|
3517
|
+
{
|
|
3518
|
+
label: field.maxLabel || "At most",
|
|
3519
|
+
value: draftMax ?? "",
|
|
3520
|
+
min: field.min,
|
|
3521
|
+
max: field.max,
|
|
3522
|
+
step: field.step,
|
|
3523
|
+
error: invalid ? "Must be \u2265 " + draftMin : void 0,
|
|
3524
|
+
placeholder: field.maxPlaceholder,
|
|
3525
|
+
onChange: (e) => apply(draftMin, e.target.value)
|
|
3526
|
+
}
|
|
3527
|
+
)
|
|
3528
|
+
] })
|
|
3529
|
+
}
|
|
3530
|
+
);
|
|
3366
3531
|
}
|
|
3367
3532
|
function DatePanel({
|
|
3368
3533
|
field,
|
|
3369
3534
|
value,
|
|
3535
|
+
anchorRef,
|
|
3370
3536
|
onChange,
|
|
3537
|
+
onRemove,
|
|
3371
3538
|
onClose
|
|
3372
3539
|
}) {
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3540
|
+
const defaultDays = field.defaultDays ?? 30;
|
|
3541
|
+
const maxDays = field.maxDays ?? 3650;
|
|
3542
|
+
const [kind, setKind] = React24.useState(value?.kind ?? "withinDays");
|
|
3543
|
+
const [daysDraft, setDaysDraft] = React24.useState(
|
|
3544
|
+
value?.kind === "withinDays" ? value.days : defaultDays
|
|
3545
|
+
);
|
|
3546
|
+
const [between, setBetween] = React24.useState(
|
|
3547
|
+
value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
|
|
3548
|
+
);
|
|
3549
|
+
const committedRef = React24.useRef(!!value);
|
|
3550
|
+
const applyWithinDays = (days) => {
|
|
3551
|
+
setDaysDraft(days);
|
|
3552
|
+
if (days != null) {
|
|
3553
|
+
committedRef.current = true;
|
|
3554
|
+
onChange({ kind: "withinDays", days });
|
|
3555
|
+
} else if (committedRef.current) {
|
|
3556
|
+
committedRef.current = false;
|
|
3557
|
+
onRemove();
|
|
3558
|
+
}
|
|
3559
|
+
};
|
|
3560
|
+
const applyBetween = (from, to) => {
|
|
3561
|
+
setBetween({ from, to });
|
|
3562
|
+
if (from && to) {
|
|
3563
|
+
committedRef.current = true;
|
|
3564
|
+
onChange({ kind: "between", from, to });
|
|
3565
|
+
} else if (committedRef.current) {
|
|
3566
|
+
committedRef.current = false;
|
|
3567
|
+
onRemove();
|
|
3568
|
+
}
|
|
3569
|
+
};
|
|
3570
|
+
const switchKind = (next) => {
|
|
3571
|
+
setKind(next);
|
|
3572
|
+
if (next === "between") {
|
|
3573
|
+
setBetween({ from: "", to: "" });
|
|
3574
|
+
if (committedRef.current) {
|
|
3575
|
+
committedRef.current = false;
|
|
3576
|
+
onRemove();
|
|
3408
3577
|
}
|
|
3409
|
-
|
|
3410
|
-
/* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", onClick: onClose, children: "Done" })
|
|
3411
|
-
] });
|
|
3412
|
-
}
|
|
3413
|
-
function PasteListPanel({
|
|
3414
|
-
field,
|
|
3415
|
-
value,
|
|
3416
|
-
onChange,
|
|
3417
|
-
onClose
|
|
3418
|
-
}) {
|
|
3419
|
-
const operator = value?.operator ?? "in";
|
|
3420
|
-
const selected = value?.values ?? [];
|
|
3421
|
-
const [text, setText] = React23.useState("");
|
|
3422
|
-
const [loading, setLoading] = React23.useState(false);
|
|
3423
|
-
const [unmatched, setUnmatched] = React23.useState([]);
|
|
3424
|
-
const [error, setError] = React23.useState(null);
|
|
3425
|
-
const wrapRef = React23.useRef(null);
|
|
3426
|
-
React23.useEffect(() => {
|
|
3427
|
-
wrapRef.current?.querySelector("textarea")?.focus();
|
|
3428
|
-
}, []);
|
|
3429
|
-
const doResolve = () => {
|
|
3430
|
-
const tokens = text.split(/[\n,]/).map((t) => t.trim()).filter(Boolean);
|
|
3431
|
-
if (!tokens.length) return;
|
|
3432
|
-
setLoading(true);
|
|
3433
|
-
setError(null);
|
|
3434
|
-
field.resolve(tokens).then((result) => {
|
|
3435
|
-
setLoading(false);
|
|
3436
|
-
setUnmatched(result.unmatched);
|
|
3437
|
-
const merged = new Map(selected.map((s) => [s.value, s]));
|
|
3438
|
-
for (const m of result.matched) merged.set(m.value, { value: m.value, label: m.label });
|
|
3439
|
-
onChange({ operator, values: Array.from(merged.values()) });
|
|
3440
|
-
setText("");
|
|
3441
|
-
}).catch((err) => {
|
|
3442
|
-
setLoading(false);
|
|
3443
|
-
setError(err instanceof Error ? err.message : "Could not resolve the pasted list.");
|
|
3444
|
-
});
|
|
3578
|
+
}
|
|
3445
3579
|
};
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3580
|
+
const confirm = () => {
|
|
3581
|
+
if (kind === "withinDays" && daysDraft != null) {
|
|
3582
|
+
committedRef.current = true;
|
|
3583
|
+
onChange({ kind: "withinDays", days: daysDraft });
|
|
3584
|
+
}
|
|
3585
|
+
onClose();
|
|
3586
|
+
};
|
|
3587
|
+
return /* @__PURE__ */ jsx45(
|
|
3588
|
+
Popover,
|
|
3589
|
+
{
|
|
3590
|
+
open: true,
|
|
3591
|
+
anchorRef,
|
|
3592
|
+
onClose,
|
|
3593
|
+
placement: "bottom-start",
|
|
3594
|
+
offset: 6,
|
|
3595
|
+
width: PANEL_WIDTH.date,
|
|
3596
|
+
label: field.label,
|
|
3597
|
+
footer: /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", onClick: confirm, children: "Done" }),
|
|
3598
|
+
children: /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3599
|
+
/* @__PURE__ */ jsx45(
|
|
3600
|
+
SegmentedControl,
|
|
3601
|
+
{
|
|
3602
|
+
"aria-label": field.label + " \u2014 relative or between dates",
|
|
3603
|
+
options: [
|
|
3604
|
+
{ value: "withinDays", label: "Within last N days" },
|
|
3605
|
+
{ value: "between", label: "Between dates" }
|
|
3606
|
+
],
|
|
3607
|
+
value: kind,
|
|
3608
|
+
onChange: (k) => switchKind(k)
|
|
3461
3609
|
}
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
] })
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
/* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", onClick: onClose, children: "Done" })
|
|
3476
|
-
] });
|
|
3610
|
+
),
|
|
3611
|
+
kind === "withinDays" ? /* @__PURE__ */ jsx45(NumberInput, { label: "Days", min: 1, max: maxDays, step: 1, value: daysDraft ?? "", onChange: (e) => applyWithinDays(e.target.value) }) : /* @__PURE__ */ jsx45(
|
|
3612
|
+
DatePicker,
|
|
3613
|
+
{
|
|
3614
|
+
range: true,
|
|
3615
|
+
label: "Date range",
|
|
3616
|
+
value: { start: between.from, end: between.to },
|
|
3617
|
+
onChange: (e) => applyBetween(e.target.value.start || "", e.target.value.end || "")
|
|
3618
|
+
}
|
|
3619
|
+
)
|
|
3620
|
+
] })
|
|
3621
|
+
}
|
|
3622
|
+
);
|
|
3477
3623
|
}
|
|
3478
3624
|
function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align = "left", className = "", ...rest }) {
|
|
3479
|
-
const [panel, setPanel] =
|
|
3480
|
-
const
|
|
3481
|
-
const
|
|
3482
|
-
const textInputRef =
|
|
3483
|
-
const optionsRef =
|
|
3625
|
+
const [panel, setPanel] = React24.useState(null);
|
|
3626
|
+
const editAnchorRef = React24.useRef(null);
|
|
3627
|
+
const addAnchorRef = React24.useRef(null);
|
|
3628
|
+
const textInputRef = React24.useRef(null);
|
|
3629
|
+
const optionsRef = React24.useRef(null);
|
|
3484
3630
|
const editingField = panel && panel.kind === "edit" ? fields.find((f) => f.key === panel.key) : void 0;
|
|
3485
|
-
const
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
const away = (e) => {
|
|
3489
|
-
const target = e.target;
|
|
3490
|
-
if (target.closest && target.closest(".fd-pop")) return;
|
|
3491
|
-
if (rootRef.current && !rootRef.current.contains(e.target)) setPanel(null);
|
|
3492
|
-
};
|
|
3493
|
-
const esc = (e) => {
|
|
3494
|
-
if (e.key === "Escape") setPanel(null);
|
|
3495
|
-
};
|
|
3496
|
-
document.addEventListener("pointerdown", away);
|
|
3497
|
-
document.addEventListener("keydown", esc);
|
|
3498
|
-
return () => {
|
|
3499
|
-
document.removeEventListener("pointerdown", away);
|
|
3500
|
-
document.removeEventListener("keydown", esc);
|
|
3501
|
-
};
|
|
3502
|
-
}, [dismissible]);
|
|
3503
|
-
React23.useEffect(() => {
|
|
3631
|
+
const startEditing = (key) => setPanel({ kind: "edit", key });
|
|
3632
|
+
const closeEditing = () => setPanel(null);
|
|
3633
|
+
React24.useEffect(() => {
|
|
3504
3634
|
if (!panel || panel.kind !== "edit") return;
|
|
3505
3635
|
if (editingField?.type === "text") {
|
|
3506
3636
|
textInputRef.current?.focus();
|
|
@@ -3510,72 +3640,77 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
|
|
|
3510
3640
|
}
|
|
3511
3641
|
}, [panel]);
|
|
3512
3642
|
const addableFields = fields.filter((f) => !isFieldSet(f, filters[f.key]));
|
|
3513
|
-
const
|
|
3514
|
-
|
|
3643
|
+
const addMenuItems = [
|
|
3644
|
+
{ kind: "section", label: "Add filter" },
|
|
3645
|
+
...addableFields.map((f) => ({ id: f.key, label: f.label, onSelect: () => startEditing(f.key) }))
|
|
3646
|
+
];
|
|
3647
|
+
return /* @__PURE__ */ jsxs39("span", { className: ["fd-filterbar", className].filter(Boolean).join(" "), ...rest, children: [
|
|
3515
3648
|
fields.map((field) => {
|
|
3516
3649
|
const value = filters[field.key];
|
|
3517
3650
|
const editing = panel?.kind === "edit" && panel.key === field.key;
|
|
3518
3651
|
if (editing && field.type === "text") {
|
|
3519
|
-
return /* @__PURE__ */ jsxs39(
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
if (e.key === "Enter") setPanel(null);
|
|
3535
|
-
else if (e.key === "Escape") {
|
|
3536
|
-
e.stopPropagation();
|
|
3537
|
-
setPanel(null);
|
|
3538
|
-
}
|
|
3539
|
-
},
|
|
3540
|
-
onBlur: () => setPanel(null),
|
|
3541
|
-
style: { all: "unset", minWidth: 90, color: "var(--text-on-surface-brand)" }
|
|
3542
|
-
}
|
|
3543
|
-
),
|
|
3544
|
-
isSet(value) ? /* @__PURE__ */ jsx45(
|
|
3545
|
-
"span",
|
|
3546
|
-
{
|
|
3547
|
-
className: "fd-tag-remove",
|
|
3548
|
-
role: "button",
|
|
3549
|
-
"aria-label": "Remove " + field.label,
|
|
3550
|
-
onClick: (e) => {
|
|
3551
|
-
e.stopPropagation();
|
|
3552
|
-
onFilterRemove(field.key);
|
|
3553
|
-
setPanel(null);
|
|
3554
|
-
},
|
|
3555
|
-
children: /* @__PURE__ */ jsx45("i", { className: "ph ph-x", "aria-hidden": "true" })
|
|
3652
|
+
return /* @__PURE__ */ jsxs39("span", { className: ["fd-tag", "fd-filterbar-text"].join(" "), children: [
|
|
3653
|
+
/* @__PURE__ */ jsx45(
|
|
3654
|
+
"input",
|
|
3655
|
+
{
|
|
3656
|
+
ref: textInputRef,
|
|
3657
|
+
className: "fd-filterbar-text-input",
|
|
3658
|
+
value: value ?? "",
|
|
3659
|
+
placeholder: field.placeholder || field.label,
|
|
3660
|
+
"aria-label": field.label,
|
|
3661
|
+
onChange: (e) => onFilterChange(field.key, e.target.value),
|
|
3662
|
+
onKeyDown: (e) => {
|
|
3663
|
+
if (e.key === "Enter") setPanel(null);
|
|
3664
|
+
else if (e.key === "Escape") {
|
|
3665
|
+
e.stopPropagation();
|
|
3666
|
+
setPanel(null);
|
|
3556
3667
|
}
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
}
|
|
3563
|
-
if (editing && field.type === "select") {
|
|
3564
|
-
const chosen = field.options.find((o) => o.value === value);
|
|
3565
|
-
return /* @__PURE__ */ jsxs39("span", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
3566
|
-
/* @__PURE__ */ jsxs39(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
|
|
3567
|
-
if (isSet(value)) onFilterRemove(field.key);
|
|
3568
|
-
setPanel(null);
|
|
3569
|
-
}, children: [
|
|
3570
|
-
field.label,
|
|
3571
|
-
chosen ? ": " + chosen.label : ""
|
|
3572
|
-
] }),
|
|
3573
|
-
/* @__PURE__ */ jsx45(LayerProvider, { zIndex, children: /* @__PURE__ */ jsx45(
|
|
3668
|
+
},
|
|
3669
|
+
onBlur: () => setPanel(null)
|
|
3670
|
+
}
|
|
3671
|
+
),
|
|
3672
|
+
isSet(value) ? /* @__PURE__ */ jsx45(
|
|
3574
3673
|
"span",
|
|
3575
3674
|
{
|
|
3576
|
-
className: "fd-
|
|
3577
|
-
|
|
3578
|
-
|
|
3675
|
+
className: "fd-tag-remove",
|
|
3676
|
+
role: "button",
|
|
3677
|
+
"aria-label": "Remove " + field.label,
|
|
3678
|
+
onClick: (e) => {
|
|
3679
|
+
e.stopPropagation();
|
|
3680
|
+
onFilterRemove(field.key);
|
|
3681
|
+
setPanel(null);
|
|
3682
|
+
},
|
|
3683
|
+
children: /* @__PURE__ */ jsx45("i", { className: "ph ph-x", "aria-hidden": "true" })
|
|
3684
|
+
}
|
|
3685
|
+
) : null
|
|
3686
|
+
] }, field.key);
|
|
3687
|
+
}
|
|
3688
|
+
const set = isFieldSet(field, value);
|
|
3689
|
+
if (!editing && !set) return null;
|
|
3690
|
+
const chipRef = editing ? editAnchorRef : void 0;
|
|
3691
|
+
const toggleOrClose = () => editing ? closeEditing() : startEditing(field.key);
|
|
3692
|
+
const remove = () => {
|
|
3693
|
+
if (set) onFilterRemove(field.key);
|
|
3694
|
+
closeEditing();
|
|
3695
|
+
};
|
|
3696
|
+
const chipText = field.label + (set ? ": " + chipLabel(field, value) : "");
|
|
3697
|
+
if (field.type === "text") {
|
|
3698
|
+
return /* @__PURE__ */ jsx45(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }, field.key);
|
|
3699
|
+
}
|
|
3700
|
+
if (field.type === "select") {
|
|
3701
|
+
return /* @__PURE__ */ jsxs39("span", { children: [
|
|
3702
|
+
/* @__PURE__ */ jsx45("span", { ref: chipRef, children: /* @__PURE__ */ jsx45(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
|
|
3703
|
+
editing ? /* @__PURE__ */ jsx45(
|
|
3704
|
+
Popover,
|
|
3705
|
+
{
|
|
3706
|
+
open: true,
|
|
3707
|
+
anchorRef: editAnchorRef,
|
|
3708
|
+
onClose: closeEditing,
|
|
3709
|
+
placement: align === "right" ? "bottom-end" : "bottom-start",
|
|
3710
|
+
offset: 6,
|
|
3711
|
+
minWidth: 200,
|
|
3712
|
+
label: field.label,
|
|
3713
|
+
children: /* @__PURE__ */ jsx45("span", { role: "listbox", "aria-label": field.label, ref: optionsRef, className: "fd-pop-list", children: field.options.map((o) => {
|
|
3579
3714
|
const on = o.value === value;
|
|
3580
3715
|
return /* @__PURE__ */ jsx45(
|
|
3581
3716
|
"button",
|
|
@@ -3583,84 +3718,72 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
|
|
|
3583
3718
|
type: "button",
|
|
3584
3719
|
role: "option",
|
|
3585
3720
|
"aria-selected": on,
|
|
3586
|
-
className: "fd-
|
|
3721
|
+
className: ["fd-opt", on ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
3587
3722
|
onClick: () => {
|
|
3588
3723
|
onFilterChange(field.key, o.value);
|
|
3589
3724
|
setPanel(null);
|
|
3590
3725
|
},
|
|
3591
|
-
|
|
3592
|
-
children: /* @__PURE__ */ jsx45("span", { className: "fd-body-sm", style: { flex: 1, fontWeight: on ? 700 : 500, color: on ? "var(--text-on-surface-brand)" : "var(--text)" }, children: o.label })
|
|
3726
|
+
children: /* @__PURE__ */ jsx45("span", { className: "fd-opt-label", children: o.label })
|
|
3593
3727
|
},
|
|
3594
3728
|
o.value
|
|
3595
3729
|
);
|
|
3596
3730
|
}) })
|
|
3597
3731
|
}
|
|
3598
|
-
)
|
|
3599
|
-
] }, field.key);
|
|
3600
|
-
}
|
|
3601
|
-
if (editing && field.type === "multiSelect") {
|
|
3602
|
-
const mv = value;
|
|
3603
|
-
return /* @__PURE__ */ jsxs39("span", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
3604
|
-
/* @__PURE__ */ jsxs39(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
|
|
3605
|
-
if (isFieldSet(field, value)) onFilterRemove(field.key);
|
|
3606
|
-
setPanel(null);
|
|
3607
|
-
}, children: [
|
|
3608
|
-
field.label,
|
|
3609
|
-
isFieldSet(field, value) ? ": " + chipLabel(field, value) : ""
|
|
3610
|
-
] }),
|
|
3611
|
-
/* @__PURE__ */ jsx45(EditPopover, { zIndex, width: 260, children: /* @__PURE__ */ jsx45(MultiSelectPanel, { field, value: mv, onChange: (v) => onFilterChange(field.key, v), onClose: () => setPanel(null) }) })
|
|
3612
|
-
] }, field.key);
|
|
3613
|
-
}
|
|
3614
|
-
if (editing && field.type === "range") {
|
|
3615
|
-
const rv = value;
|
|
3616
|
-
return /* @__PURE__ */ jsxs39("span", { style: { position: "relative", display: "inline-flex" }, children: [
|
|
3617
|
-
/* @__PURE__ */ jsxs39(Tag, { selected: true, onClick: () => setPanel(null), onRemove: () => {
|
|
3618
|
-
if (isFieldSet(field, value)) onFilterRemove(field.key);
|
|
3619
|
-
setPanel(null);
|
|
3620
|
-
}, children: [
|
|
3621
|
-
field.label,
|
|
3622
|
-
isFieldSet(field, value) ? ": " + chipLabel(field, value) : ""
|
|
3623
|
-
] }),
|
|
3624
|
-
/* @__PURE__ */ jsx45(EditPopover, { zIndex, width: 240, children: /* @__PURE__ */ jsx45(RangePanel, { field, value: rv, onChange: (v) => onFilterChange(field.key, v), onClose: () => setPanel(null) }) })
|
|
3732
|
+
) : null
|
|
3625
3733
|
] }, field.key);
|
|
3626
3734
|
}
|
|
3627
|
-
if (
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
/* @__PURE__ */
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3735
|
+
if (field.type === "multiSelect") {
|
|
3736
|
+
return /* @__PURE__ */ jsxs39("span", { children: [
|
|
3737
|
+
/* @__PURE__ */ jsx45("span", { ref: chipRef, children: /* @__PURE__ */ jsx45(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
|
|
3738
|
+
editing ? /* @__PURE__ */ jsx45(
|
|
3739
|
+
MultiSelectPanel,
|
|
3740
|
+
{
|
|
3741
|
+
field,
|
|
3742
|
+
value,
|
|
3743
|
+
anchorRef: editAnchorRef,
|
|
3744
|
+
onChange: (v) => onFilterChange(field.key, v),
|
|
3745
|
+
onRemove: () => onFilterRemove(field.key),
|
|
3746
|
+
onClose: closeEditing
|
|
3747
|
+
}
|
|
3748
|
+
) : null
|
|
3638
3749
|
] }, field.key);
|
|
3639
3750
|
}
|
|
3640
|
-
if (
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
/* @__PURE__ */
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3751
|
+
if (field.type === "range") {
|
|
3752
|
+
return /* @__PURE__ */ jsxs39("span", { children: [
|
|
3753
|
+
/* @__PURE__ */ jsx45("span", { ref: chipRef, children: /* @__PURE__ */ jsx45(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
|
|
3754
|
+
editing ? /* @__PURE__ */ jsx45(
|
|
3755
|
+
RangePanel,
|
|
3756
|
+
{
|
|
3757
|
+
field,
|
|
3758
|
+
value,
|
|
3759
|
+
anchorRef: editAnchorRef,
|
|
3760
|
+
onChange: (v) => onFilterChange(field.key, v),
|
|
3761
|
+
onRemove: () => onFilterRemove(field.key),
|
|
3762
|
+
onClose: closeEditing
|
|
3763
|
+
}
|
|
3764
|
+
) : null
|
|
3651
3765
|
] }, field.key);
|
|
3652
3766
|
}
|
|
3653
|
-
if (
|
|
3654
|
-
return /* @__PURE__ */ jsxs39(
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3767
|
+
if (field.type === "date") {
|
|
3768
|
+
return /* @__PURE__ */ jsxs39("span", { children: [
|
|
3769
|
+
/* @__PURE__ */ jsx45("span", { ref: chipRef, children: /* @__PURE__ */ jsx45(Tag, { selected: true, onClick: toggleOrClose, onRemove: remove, children: chipText }) }),
|
|
3770
|
+
editing ? /* @__PURE__ */ jsx45(
|
|
3771
|
+
DatePanel,
|
|
3772
|
+
{
|
|
3773
|
+
field,
|
|
3774
|
+
value,
|
|
3775
|
+
anchorRef: editAnchorRef,
|
|
3776
|
+
onChange: (v) => onFilterChange(field.key, v),
|
|
3777
|
+
onRemove: () => onFilterRemove(field.key),
|
|
3778
|
+
onClose: closeEditing
|
|
3779
|
+
}
|
|
3780
|
+
) : null
|
|
3658
3781
|
] }, field.key);
|
|
3659
3782
|
}
|
|
3660
3783
|
return null;
|
|
3661
3784
|
}),
|
|
3662
|
-
addableFields.length > 0 ? /* @__PURE__ */ jsxs39("span", {
|
|
3663
|
-
/* @__PURE__ */ jsx45(
|
|
3785
|
+
addableFields.length > 0 ? /* @__PURE__ */ jsxs39("span", { children: [
|
|
3786
|
+
/* @__PURE__ */ jsx45("span", { ref: addAnchorRef, children: /* @__PURE__ */ jsx45(
|
|
3664
3787
|
Button,
|
|
3665
3788
|
{
|
|
3666
3789
|
size: "sm",
|
|
@@ -3671,29 +3794,20 @@ function FilterBar({ fields = [], filters, onFilterChange, onFilterRemove, align
|
|
|
3671
3794
|
onClick: () => setPanel(panel?.kind === "add" ? null : { kind: "add" }),
|
|
3672
3795
|
children: "Filter"
|
|
3673
3796
|
}
|
|
3674
|
-
),
|
|
3675
|
-
|
|
3676
|
-
|
|
3797
|
+
) }),
|
|
3798
|
+
/* @__PURE__ */ jsx45(
|
|
3799
|
+
Popover,
|
|
3677
3800
|
{
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
role: "menuitem",
|
|
3687
|
-
className: "fd-row",
|
|
3688
|
-
onClick: () => startEditing(f.key),
|
|
3689
|
-
style: { gap: 8, padding: "4px 8px", borderRadius: 7, border: 0, width: "100%", textAlign: "left", cursor: "pointer", background: "transparent" },
|
|
3690
|
-
children: /* @__PURE__ */ jsx45("span", { className: "fd-body-sm", style: { flex: 1, color: "var(--text)" }, children: f.label })
|
|
3691
|
-
},
|
|
3692
|
-
f.key
|
|
3693
|
-
))
|
|
3694
|
-
] })
|
|
3801
|
+
open: panel?.kind === "add",
|
|
3802
|
+
anchorRef: addAnchorRef,
|
|
3803
|
+
onClose: closeEditing,
|
|
3804
|
+
placement: align === "right" ? "bottom-end" : "bottom-start",
|
|
3805
|
+
minWidth: 200,
|
|
3806
|
+
role: "menu",
|
|
3807
|
+
label: "Add filter",
|
|
3808
|
+
children: /* @__PURE__ */ jsx45(Menu, { items: addMenuItems })
|
|
3695
3809
|
}
|
|
3696
|
-
)
|
|
3810
|
+
)
|
|
3697
3811
|
] }) : null
|
|
3698
3812
|
] });
|
|
3699
3813
|
}
|
|
@@ -3725,7 +3839,7 @@ function StatTile({ label, value, sub, delta, invert = false, loading = false, c
|
|
|
3725
3839
|
}
|
|
3726
3840
|
|
|
3727
3841
|
// src/components/data/CodeBlock.tsx
|
|
3728
|
-
import * as
|
|
3842
|
+
import * as React25 from "react";
|
|
3729
3843
|
import { Fragment as Fragment6, jsx as jsx47, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
3730
3844
|
var JS_KW = /^(?:const|let|var|function|return|if|else|for|while|do|new|class|extends|import|from|export|default|await|async|try|catch|finally|throw|typeof|instanceof|of|in|switch|case|break|continue|delete|void|yield|static|get|set|this|super|interface|type|enum|implements|public|private|protected|readonly|abstract|as|satisfies|declare|namespace)$/;
|
|
3731
3845
|
var RULES = {
|
|
@@ -3881,13 +3995,13 @@ function tokenize(src, lang) {
|
|
|
3881
3995
|
return out;
|
|
3882
3996
|
}
|
|
3883
3997
|
function Highlighted({ code, lang }) {
|
|
3884
|
-
const toks =
|
|
3998
|
+
const toks = React25.useMemo(() => tokenize(code, lang), [code, lang]);
|
|
3885
3999
|
return /* @__PURE__ */ jsx47(Fragment6, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx47("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
|
|
3886
4000
|
}
|
|
3887
4001
|
function useCopy() {
|
|
3888
|
-
const [done, setDone] =
|
|
3889
|
-
const timer =
|
|
3890
|
-
|
|
4002
|
+
const [done, setDone] = React25.useState(false);
|
|
4003
|
+
const timer = React25.useRef(null);
|
|
4004
|
+
React25.useEffect(() => () => {
|
|
3891
4005
|
if (timer.current) clearTimeout(timer.current);
|
|
3892
4006
|
}, []);
|
|
3893
4007
|
const copy = (text) => {
|
|
@@ -3923,8 +4037,8 @@ function CodeBlock({
|
|
|
3923
4037
|
actions,
|
|
3924
4038
|
className = ""
|
|
3925
4039
|
}) {
|
|
3926
|
-
const [wrap, setWrap] =
|
|
3927
|
-
const [open, setOpen] =
|
|
4040
|
+
const [wrap, setWrap] = React25.useState(!!wrapDefault);
|
|
4041
|
+
const [open, setOpen] = React25.useState(false);
|
|
3928
4042
|
const [copied, copy] = useCopy();
|
|
3929
4043
|
const body = String(code).replace(/\n$/, "");
|
|
3930
4044
|
const lines = body.split("\n");
|
|
@@ -3953,7 +4067,7 @@ function CodeBlock({
|
|
|
3953
4067
|
] });
|
|
3954
4068
|
}
|
|
3955
4069
|
function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" }) {
|
|
3956
|
-
const [open, setOpen] =
|
|
4070
|
+
const [open, setOpen] = React25.useState(false);
|
|
3957
4071
|
const [copied, copy] = useCopy();
|
|
3958
4072
|
const raw = String(diff).replace(/\n$/, "");
|
|
3959
4073
|
const all = raw.split("\n").map((line) => {
|
|
@@ -3999,7 +4113,7 @@ function DiffBlock({ diff = "", filename, collapseAfter = 34, className = "" })
|
|
|
3999
4113
|
}
|
|
4000
4114
|
|
|
4001
4115
|
// src/components/data/Markdown.tsx
|
|
4002
|
-
import * as
|
|
4116
|
+
import * as React26 from "react";
|
|
4003
4117
|
import { jsx as jsx48, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
4004
4118
|
var SAFE = /^(?:https?:|mailto:|tel:|#|\/|\.{1,2}\/)/i;
|
|
4005
4119
|
var safeHref = (h) => SAFE.test(String(h || "").trim()) ? String(h).trim() : null;
|
|
@@ -4038,7 +4152,7 @@ function inlineNodes(src, ctx, keyBase) {
|
|
|
4038
4152
|
const href = safeHref(g(2));
|
|
4039
4153
|
out.push(href ? /* @__PURE__ */ jsx48("a", { href, title: g(3) || void 0, target: /^https?:/i.test(href) ? "_blank" : void 0, rel: "noopener noreferrer", onClick: ctx.onLinkClick, children: inlineNodes(g(1), ctx, key()) }, key()) : /* @__PURE__ */ jsx48("span", { children: g(1) }, key()));
|
|
4040
4154
|
} else if (kind === "cite") {
|
|
4041
|
-
out.push(ctx.renderCitation ? /* @__PURE__ */ jsx48(
|
|
4155
|
+
out.push(ctx.renderCitation ? /* @__PURE__ */ jsx48(React26.Fragment, { children: ctx.renderCitation(g(1)) }, key()) : /* @__PURE__ */ jsx48("sup", { className: "fd-md-cite", children: g(1) }, key()));
|
|
4042
4156
|
} else if (kind === "strongem") out.push(/* @__PURE__ */ jsx48("strong", { children: /* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1), ctx, key()) }) }, key()));
|
|
4043
4157
|
else if (kind === "strong") out.push(/* @__PURE__ */ jsx48("strong", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
|
|
4044
4158
|
else if (kind === "em") out.push(/* @__PURE__ */ jsx48("em", { children: inlineNodes(g(1) || g(2), ctx, key()) }, key()));
|
|
@@ -4203,7 +4317,7 @@ function Markdown({
|
|
|
4203
4317
|
onLinkClick
|
|
4204
4318
|
}) {
|
|
4205
4319
|
const ctx = { allowImages, headingOffset, renderCitation, onLinkClick, codeProps };
|
|
4206
|
-
const blocks =
|
|
4320
|
+
const blocks = React26.useMemo(() => parseBlocks(source), [source]);
|
|
4207
4321
|
return /* @__PURE__ */ jsx48("div", { className: ["fd-md", className].filter(Boolean).join(" "), children: renderBlocks(blocks, ctx, "b") });
|
|
4208
4322
|
}
|
|
4209
4323
|
function MarkdownInline({ source = "", className = "", onLinkClick }) {
|
|
@@ -4317,7 +4431,7 @@ function Pagination({
|
|
|
4317
4431
|
}
|
|
4318
4432
|
|
|
4319
4433
|
// src/components/data/RelativeTime.tsx
|
|
4320
|
-
import * as
|
|
4434
|
+
import * as React27 from "react";
|
|
4321
4435
|
import { jsxs as jsxs45 } from "react/jsx-runtime";
|
|
4322
4436
|
var MIN = 6e4;
|
|
4323
4437
|
var HOUR = 36e5;
|
|
@@ -4369,9 +4483,9 @@ function formatDuration(ms) {
|
|
|
4369
4483
|
return h + "h" + (m % 60 ? " " + m % 60 + "m" : "");
|
|
4370
4484
|
}
|
|
4371
4485
|
function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true, ...rest }) {
|
|
4372
|
-
const [, force] =
|
|
4486
|
+
const [, force] = React27.useState(0);
|
|
4373
4487
|
const t = value instanceof Date ? value.getTime() : Date.parse(value);
|
|
4374
|
-
|
|
4488
|
+
React27.useEffect(() => {
|
|
4375
4489
|
if (!refresh2 || !t || isNaN(t)) return;
|
|
4376
4490
|
const abs = Math.abs(t - Date.now());
|
|
4377
4491
|
if (abs > DAY) return;
|
|
@@ -4387,7 +4501,7 @@ function RelativeTime({ value, prefix, className = "", refresh: refresh2 = true,
|
|
|
4387
4501
|
}
|
|
4388
4502
|
|
|
4389
4503
|
// src/components/data/ScoreMeter.tsx
|
|
4390
|
-
import * as
|
|
4504
|
+
import * as React28 from "react";
|
|
4391
4505
|
import { jsx as jsx51, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
4392
4506
|
function ScoreMeter({
|
|
4393
4507
|
score,
|
|
@@ -4402,10 +4516,10 @@ function ScoreMeter({
|
|
|
4402
4516
|
breakdownWidth = 280,
|
|
4403
4517
|
className = ""
|
|
4404
4518
|
}) {
|
|
4405
|
-
const [open, setOpen] =
|
|
4406
|
-
const ref =
|
|
4519
|
+
const [open, setOpen] = React28.useState(false);
|
|
4520
|
+
const ref = React28.useRef(null);
|
|
4407
4521
|
const value = Math.max(0, Math.min(100, Math.round(Number(score) || 0)));
|
|
4408
|
-
const body = /* @__PURE__ */ jsxs46(
|
|
4522
|
+
const body = /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
|
|
4409
4523
|
/* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-rail", style: { width }, "aria-hidden": "true", children: /* @__PURE__ */ jsx51(SegmentedMeter, { total: 100, segments, height, showTotal: false, showPercent: false, legend: false }) }),
|
|
4410
4524
|
showScore ? /* @__PURE__ */ jsx51("span", { className: "fd-scoremeter-value fd-tabular", "aria-hidden": "true", children: value }) : null
|
|
4411
4525
|
] });
|
|
@@ -4424,7 +4538,7 @@ function ScoreMeter({
|
|
|
4424
4538
|
}
|
|
4425
4539
|
);
|
|
4426
4540
|
}
|
|
4427
|
-
return /* @__PURE__ */ jsxs46(
|
|
4541
|
+
return /* @__PURE__ */ jsxs46(React28.Fragment, { children: [
|
|
4428
4542
|
/* @__PURE__ */ jsx51(
|
|
4429
4543
|
"button",
|
|
4430
4544
|
{
|
|
@@ -4455,13 +4569,13 @@ function ScoreMeter({
|
|
|
4455
4569
|
}
|
|
4456
4570
|
|
|
4457
4571
|
// src/components/data/SortMenu.tsx
|
|
4458
|
-
import * as
|
|
4572
|
+
import * as React29 from "react";
|
|
4459
4573
|
import { jsx as jsx52, jsxs as jsxs47 } from "react/jsx-runtime";
|
|
4460
4574
|
function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", className = "", ...rest }) {
|
|
4461
|
-
const [open, setOpen] =
|
|
4575
|
+
const [open, setOpen] = React29.useState(false);
|
|
4462
4576
|
const zIndex = useOverlayLayer(open);
|
|
4463
|
-
const ref =
|
|
4464
|
-
|
|
4577
|
+
const ref = React29.useRef(null);
|
|
4578
|
+
React29.useEffect(() => {
|
|
4465
4579
|
if (!open) return;
|
|
4466
4580
|
const away = (e) => {
|
|
4467
4581
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -4523,7 +4637,7 @@ function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", cla
|
|
|
4523
4637
|
}
|
|
4524
4638
|
|
|
4525
4639
|
// src/components/data/StepList.tsx
|
|
4526
|
-
import * as
|
|
4640
|
+
import * as React30 from "react";
|
|
4527
4641
|
import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
|
|
4528
4642
|
var KIND_ICON = { tool: "wrench", search: "magnifying-glass", read: "file-text", write: "pencil-simple", reasoning: "brain", run: "terminal", fetch: "cloud-arrow-down" };
|
|
4529
4643
|
function StepList({
|
|
@@ -4536,9 +4650,9 @@ function StepList({
|
|
|
4536
4650
|
dense = false,
|
|
4537
4651
|
className = ""
|
|
4538
4652
|
}) {
|
|
4539
|
-
const [openState, setOpenState] =
|
|
4653
|
+
const [openState, setOpenState] = React30.useState(defaultOpen);
|
|
4540
4654
|
const open = openProp == null ? openState : openProp;
|
|
4541
|
-
const [expanded, setExpanded] =
|
|
4655
|
+
const [expanded, setExpanded] = React30.useState({});
|
|
4542
4656
|
if (!steps.length) return null;
|
|
4543
4657
|
const running = steps.some((s) => s.status === "running");
|
|
4544
4658
|
const failed = steps.filter((s) => s.status === "error").length;
|
|
@@ -4596,7 +4710,7 @@ function StepList({
|
|
|
4596
4710
|
}
|
|
4597
4711
|
|
|
4598
4712
|
// src/components/data/VirtualList.tsx
|
|
4599
|
-
import * as
|
|
4713
|
+
import * as React31 from "react";
|
|
4600
4714
|
import { jsx as jsx54, jsxs as jsxs49 } from "react/jsx-runtime";
|
|
4601
4715
|
var VIRTUAL_LIST_BUFFER_ROWS = 20;
|
|
4602
4716
|
var VIRTUAL_LIST_ROW_GAP = 6;
|
|
@@ -4648,35 +4762,35 @@ function VirtualList({
|
|
|
4648
4762
|
className = "",
|
|
4649
4763
|
style
|
|
4650
4764
|
}) {
|
|
4651
|
-
const [scrollTop, setScrollTop] =
|
|
4652
|
-
const loaded =
|
|
4765
|
+
const [scrollTop, setScrollTop] = React31.useState(0);
|
|
4766
|
+
const loaded = React31.useMemo(
|
|
4653
4767
|
() => ({ firstItemIndex, count: items.length }),
|
|
4654
4768
|
[firstItemIndex, items.length]
|
|
4655
4769
|
);
|
|
4656
4770
|
const total = Math.max(totalCount == null ? 0 : totalCount, firstItemIndex + items.length);
|
|
4657
4771
|
const bandKey = firstItemIndex + ":" + items.length;
|
|
4658
|
-
const win =
|
|
4772
|
+
const win = React31.useMemo(
|
|
4659
4773
|
() => computeVirtualWindow({ scrollTop, viewportHeight: height, itemHeight, itemCount: total, loaded }),
|
|
4660
4774
|
[scrollTop, height, itemHeight, total, loaded]
|
|
4661
4775
|
);
|
|
4662
|
-
const need =
|
|
4776
|
+
const need = React31.useMemo(() => computeNeedMore(win, loaded), [win, loaded]);
|
|
4663
4777
|
const wantStart = need.start && hasMore?.start !== false;
|
|
4664
4778
|
const wantEnd = need.end && hasMore?.end !== false;
|
|
4665
4779
|
const bucket2 = Math.floor(win.visibleStart / VIRTUAL_LIST_BUFFER_ROWS);
|
|
4666
4780
|
const askKey = bandKey + "@" + bucket2;
|
|
4667
|
-
const asked =
|
|
4668
|
-
|
|
4781
|
+
const asked = React31.useRef({ start: null, end: null });
|
|
4782
|
+
React31.useEffect(() => {
|
|
4669
4783
|
if (loading || !wantStart || asked.current.start === askKey) return;
|
|
4670
4784
|
asked.current.start = askKey;
|
|
4671
4785
|
onNeedMore("start", win);
|
|
4672
4786
|
}, [wantStart, loading, askKey, onNeedMore, win]);
|
|
4673
|
-
|
|
4787
|
+
React31.useEffect(() => {
|
|
4674
4788
|
if (loading || !wantEnd || asked.current.end === askKey) return;
|
|
4675
4789
|
asked.current.end = askKey;
|
|
4676
4790
|
onNeedMore("end", win);
|
|
4677
4791
|
}, [wantEnd, loading, askKey, onNeedMore, win]);
|
|
4678
|
-
const lastKeysRef =
|
|
4679
|
-
const newKeys =
|
|
4792
|
+
const lastKeysRef = React31.useRef(null);
|
|
4793
|
+
const newKeys = React31.useMemo(() => {
|
|
4680
4794
|
const prev = lastKeysRef.current;
|
|
4681
4795
|
if (!prev) return /* @__PURE__ */ new Set();
|
|
4682
4796
|
const fresh = /* @__PURE__ */ new Set();
|
|
@@ -4686,7 +4800,7 @@ function VirtualList({
|
|
|
4686
4800
|
}
|
|
4687
4801
|
return fresh;
|
|
4688
4802
|
}, [items]);
|
|
4689
|
-
|
|
4803
|
+
React31.useEffect(() => {
|
|
4690
4804
|
lastKeysRef.current = new Set(items.map(keyOf));
|
|
4691
4805
|
}, [items]);
|
|
4692
4806
|
const gapStyle = { "--fd-vlist-gap": rowGap + "px" };
|
|
@@ -4731,7 +4845,7 @@ function VirtualList({
|
|
|
4731
4845
|
}
|
|
4732
4846
|
|
|
4733
4847
|
// src/components/data/EntityRow.tsx
|
|
4734
|
-
import * as
|
|
4848
|
+
import * as React32 from "react";
|
|
4735
4849
|
import { jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
|
|
4736
4850
|
var ENTITY_ROW_METRIC_WIDTH = 74;
|
|
4737
4851
|
var METRIC_TONE_COLOR = {
|
|
@@ -4744,7 +4858,7 @@ function EntityRowMetrics({ metrics, className = "", style }) {
|
|
|
4744
4858
|
const key = m.id || m.label || i;
|
|
4745
4859
|
const className2 = ["fd-erow-metric", m.tone && m.tone !== "default" ? "is-" + m.tone : "", m.tooltip != null ? "is-described" : ""].filter(Boolean).join(" ");
|
|
4746
4860
|
const tone = { color: METRIC_TONE_COLOR[m.tone || "default"] };
|
|
4747
|
-
const content = /* @__PURE__ */ jsxs50(
|
|
4861
|
+
const content = /* @__PURE__ */ jsxs50(React32.Fragment, { children: [
|
|
4748
4862
|
m.icon ? /* @__PURE__ */ jsx55("i", { className: "ph ph-" + m.icon, "aria-hidden": "true" }) : null,
|
|
4749
4863
|
/* @__PURE__ */ jsx55("span", { className: "fd-erow-metric-value fd-tabular", children: m.value }),
|
|
4750
4864
|
/* @__PURE__ */ jsx55("span", { className: "fd-sr", children: " " + m.label })
|
|
@@ -4836,7 +4950,7 @@ function EntityRow({
|
|
|
4836
4950
|
}
|
|
4837
4951
|
|
|
4838
4952
|
// src/components/data/TransferList.tsx
|
|
4839
|
-
import * as
|
|
4953
|
+
import * as React33 from "react";
|
|
4840
4954
|
import { createPortal as createPortal4 } from "react-dom";
|
|
4841
4955
|
|
|
4842
4956
|
// src/components/forms/Input.tsx
|
|
@@ -4942,12 +5056,12 @@ function TransferList({
|
|
|
4942
5056
|
listHeight = 440,
|
|
4943
5057
|
className = ""
|
|
4944
5058
|
}) {
|
|
4945
|
-
const [drag, setDrag] =
|
|
4946
|
-
const [dragOverSide, setDragOverSide] =
|
|
4947
|
-
const dragRef =
|
|
4948
|
-
const armedRef =
|
|
4949
|
-
const sideRefs =
|
|
4950
|
-
const hitTestSide =
|
|
5059
|
+
const [drag, setDrag] = React33.useState(null);
|
|
5060
|
+
const [dragOverSide, setDragOverSide] = React33.useState(null);
|
|
5061
|
+
const dragRef = React33.useRef(null);
|
|
5062
|
+
const armedRef = React33.useRef(null);
|
|
5063
|
+
const sideRefs = React33.useRef({ left: null, right: null });
|
|
5064
|
+
const hitTestSide = React33.useCallback((x, y) => {
|
|
4951
5065
|
for (const side of ["left", "right"]) {
|
|
4952
5066
|
const el = sideRefs.current[side];
|
|
4953
5067
|
if (!el) continue;
|
|
@@ -4956,7 +5070,7 @@ function TransferList({
|
|
|
4956
5070
|
}
|
|
4957
5071
|
return null;
|
|
4958
5072
|
}, []);
|
|
4959
|
-
const findItem =
|
|
5073
|
+
const findItem = React33.useCallback(
|
|
4960
5074
|
(side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
|
|
4961
5075
|
[left.items, right.items, keyOf]
|
|
4962
5076
|
);
|
|
@@ -4978,7 +5092,7 @@ function TransferList({
|
|
|
4978
5092
|
height: rect.height
|
|
4979
5093
|
};
|
|
4980
5094
|
};
|
|
4981
|
-
|
|
5095
|
+
React33.useEffect(() => {
|
|
4982
5096
|
const onMovePointer = (e) => {
|
|
4983
5097
|
const armed = armedRef.current;
|
|
4984
5098
|
if (!armed || e.pointerId !== armed.pointerId) return;
|
|
@@ -5031,7 +5145,7 @@ function TransferList({
|
|
|
5031
5145
|
window.removeEventListener("pointercancel", endDrag);
|
|
5032
5146
|
};
|
|
5033
5147
|
}, [hitTestSide, findItem, onMove]);
|
|
5034
|
-
|
|
5148
|
+
React33.useEffect(() => {
|
|
5035
5149
|
if (!drag || typeof document === "undefined") return;
|
|
5036
5150
|
const prev = document.body.style.userSelect;
|
|
5037
5151
|
document.body.style.userSelect = "none";
|
|
@@ -5139,11 +5253,11 @@ function TransferList({
|
|
|
5139
5253
|
}
|
|
5140
5254
|
|
|
5141
5255
|
// src/components/forms/Checkbox.tsx
|
|
5142
|
-
import * as
|
|
5256
|
+
import * as React34 from "react";
|
|
5143
5257
|
import { jsx as jsx59, jsxs as jsxs53 } from "react/jsx-runtime";
|
|
5144
5258
|
function Checkbox({ label, description, card = false, indeterminate = false, className = "", ...rest }) {
|
|
5145
|
-
const ref =
|
|
5146
|
-
|
|
5259
|
+
const ref = React34.useRef(null);
|
|
5260
|
+
React34.useEffect(() => {
|
|
5147
5261
|
if (ref.current) ref.current.indeterminate = indeterminate;
|
|
5148
5262
|
}, [indeterminate]);
|
|
5149
5263
|
return /* @__PURE__ */ jsxs53("label", { className: ["fd-choice", card ? "fd-choice-card" : "", className].filter(Boolean).join(" "), children: [
|
|
@@ -5187,7 +5301,7 @@ function Switch({ label, description, className = "", ...rest }) {
|
|
|
5187
5301
|
}
|
|
5188
5302
|
|
|
5189
5303
|
// src/components/forms/Select.tsx
|
|
5190
|
-
import * as
|
|
5304
|
+
import * as React35 from "react";
|
|
5191
5305
|
import { jsx as jsx62, jsxs as jsxs56 } from "react/jsx-runtime";
|
|
5192
5306
|
var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
|
|
5193
5307
|
function Select({
|
|
@@ -5214,13 +5328,12 @@ function Select({
|
|
|
5214
5328
|
const isOn = (v) => multiple ? vals.includes(v) : v === value;
|
|
5215
5329
|
const hasSearch = searchable === void 0 ? opts.length > 8 : searchable;
|
|
5216
5330
|
const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
|
|
5217
|
-
const [open, setOpen] =
|
|
5218
|
-
const [q, setQ] =
|
|
5219
|
-
const
|
|
5220
|
-
const
|
|
5221
|
-
const
|
|
5222
|
-
const
|
|
5223
|
-
const typeBuf = React34.useRef({ s: "", t: 0 });
|
|
5331
|
+
const [open, setOpen] = React35.useState(false);
|
|
5332
|
+
const [q, setQ] = React35.useState("");
|
|
5333
|
+
const rootRef = React35.useRef(null);
|
|
5334
|
+
const boxRef = React35.useRef(null);
|
|
5335
|
+
const listRef = React35.useRef(null);
|
|
5336
|
+
const typeBuf = React35.useRef({ s: "", t: 0 });
|
|
5224
5337
|
const selected = multiple ? null : opts.find((o) => o.value === value);
|
|
5225
5338
|
const chosen = multiple ? opts.filter((o) => vals.includes(o.value)) : [];
|
|
5226
5339
|
const boxText = multiple ? chosen.length === 0 ? placeholder : chosen.length === 1 ? chosen[0].label : summary ? summary(chosen.map((o) => o.value)) : chosen.length + " selected" : selected ? selected.label : placeholder;
|
|
@@ -5237,23 +5350,16 @@ function Select({
|
|
|
5237
5350
|
fire(o.value);
|
|
5238
5351
|
setOpen(false);
|
|
5239
5352
|
};
|
|
5353
|
+
const nav = useListboxNav({ count: visible.length, onSelect: (i) => pick(visible[i]) });
|
|
5354
|
+
useListboxScrollIntoView(nav.active, listRef, open);
|
|
5240
5355
|
const toggle = () => {
|
|
5241
5356
|
if (disabled || loading) return;
|
|
5242
5357
|
if (!open) {
|
|
5243
5358
|
setQ("");
|
|
5244
|
-
setActive(Math.max(0, opts.findIndex((o) => isOn(o.value))));
|
|
5359
|
+
nav.setActive(Math.max(0, opts.findIndex((o) => isOn(o.value))));
|
|
5245
5360
|
}
|
|
5246
5361
|
setOpen(!open);
|
|
5247
5362
|
};
|
|
5248
|
-
React34.useEffect(() => {
|
|
5249
|
-
if (!open || active < 0 || !listRef.current) return;
|
|
5250
|
-
const el = listRef.current.querySelector('[data-i="' + active + '"]');
|
|
5251
|
-
if (el) {
|
|
5252
|
-
const L = listRef.current, top = el.offsetTop - L.offsetTop;
|
|
5253
|
-
if (top < L.scrollTop) L.scrollTop = top - 4;
|
|
5254
|
-
else if (top + el.offsetHeight > L.scrollTop + L.clientHeight) L.scrollTop = top + el.offsetHeight - L.clientHeight + 4;
|
|
5255
|
-
}
|
|
5256
|
-
}, [active, open]);
|
|
5257
5363
|
const onKey = (e) => {
|
|
5258
5364
|
if (disabled) return;
|
|
5259
5365
|
if (!open && (e.key === "Enter" || e.key === " " || e.key === "ArrowDown" || e.key === "ArrowUp")) {
|
|
@@ -5275,19 +5381,19 @@ function Select({
|
|
|
5275
5381
|
setOpen(false);
|
|
5276
5382
|
} else if (e.key === "ArrowDown") {
|
|
5277
5383
|
e.preventDefault();
|
|
5278
|
-
|
|
5384
|
+
nav.move(1);
|
|
5279
5385
|
} else if (e.key === "ArrowUp") {
|
|
5280
5386
|
e.preventDefault();
|
|
5281
|
-
|
|
5387
|
+
nav.move(-1);
|
|
5282
5388
|
} else if (e.key === "Home") {
|
|
5283
5389
|
e.preventDefault();
|
|
5284
|
-
|
|
5390
|
+
nav.first();
|
|
5285
5391
|
} else if (e.key === "End") {
|
|
5286
5392
|
e.preventDefault();
|
|
5287
|
-
|
|
5393
|
+
nav.last();
|
|
5288
5394
|
} else if (e.key === "Enter") {
|
|
5289
5395
|
e.preventDefault();
|
|
5290
|
-
|
|
5396
|
+
nav.activate();
|
|
5291
5397
|
} else if (e.key === "Tab") setOpen(false);
|
|
5292
5398
|
};
|
|
5293
5399
|
const groups = [];
|
|
@@ -5360,13 +5466,13 @@ function Select({
|
|
|
5360
5466
|
onKeyDown: onKey,
|
|
5361
5467
|
onChange: (e) => {
|
|
5362
5468
|
setQ(e.target.value);
|
|
5363
|
-
setActive(0);
|
|
5469
|
+
nav.setActive(0);
|
|
5364
5470
|
}
|
|
5365
5471
|
}
|
|
5366
5472
|
),
|
|
5367
5473
|
q ? /* @__PURE__ */ jsx62("span", { className: "fd-body-sm fd-muted", children: visible.length }) : null
|
|
5368
5474
|
] }) : void 0,
|
|
5369
|
-
footer: multiple ? /* @__PURE__ */ jsxs56(
|
|
5475
|
+
footer: multiple ? /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
|
|
5370
5476
|
/* @__PURE__ */ jsx62(
|
|
5371
5477
|
"button",
|
|
5372
5478
|
{
|
|
@@ -5397,7 +5503,7 @@ function Select({
|
|
|
5397
5503
|
'Nothing matches "',
|
|
5398
5504
|
q,
|
|
5399
5505
|
'".'
|
|
5400
|
-
] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(
|
|
5506
|
+
] }) : groups.map((grp) => /* @__PURE__ */ jsxs56(React35.Fragment, { children: [
|
|
5401
5507
|
grp.g ? /* @__PURE__ */ jsx62("div", { className: "fd-pop-group", children: grp.g }) : null,
|
|
5402
5508
|
grp.items.map(({ o, i }) => /* @__PURE__ */ jsxs56(
|
|
5403
5509
|
"button",
|
|
@@ -5407,8 +5513,8 @@ function Select({
|
|
|
5407
5513
|
role: "option",
|
|
5408
5514
|
"aria-selected": isOn(o.value),
|
|
5409
5515
|
"aria-disabled": o.disabled || void 0,
|
|
5410
|
-
className: ["fd-opt", i === active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
5411
|
-
onMouseEnter: () => setActive(i),
|
|
5516
|
+
className: ["fd-opt", i === nav.active ? "is-active" : "", isOn(o.value) ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
5517
|
+
onMouseEnter: () => nav.setActive(i),
|
|
5412
5518
|
onClick: () => pick(o),
|
|
5413
5519
|
children: [
|
|
5414
5520
|
multiple ? /* @__PURE__ */ jsx62("span", { "aria-hidden": "true", style: { flex: "none", display: "grid", placeItems: "center", width: 16, height: 16, borderRadius: 4, border: "1.5px solid " + (isOn(o.value) ? "var(--brand)" : "var(--border-strong, var(--border))"), background: isOn(o.value) ? "var(--brand)" : "var(--surface)", color: "#fff" }, children: isOn(o.value) ? /* @__PURE__ */ jsx62("i", { className: "ph ph-check", style: { fontSize: 11 } }) : null }) : null,
|
|
@@ -5434,7 +5540,7 @@ function Select({
|
|
|
5434
5540
|
}
|
|
5435
5541
|
|
|
5436
5542
|
// src/components/forms/TimePicker.tsx
|
|
5437
|
-
import * as
|
|
5543
|
+
import * as React36 from "react";
|
|
5438
5544
|
import { jsx as jsx63, jsxs as jsxs57 } from "react/jsx-runtime";
|
|
5439
5545
|
var pad = (n) => String(n).padStart(2, "0");
|
|
5440
5546
|
function ClockFace({ value = "09:00", onChange }) {
|
|
@@ -5443,9 +5549,9 @@ function ClockFace({ value = "09:00", onChange }) {
|
|
|
5443
5549
|
if (isNaN(m)) m = 0;
|
|
5444
5550
|
const pm = h24 >= 12;
|
|
5445
5551
|
const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
|
|
5446
|
-
const [mode2, setMode] =
|
|
5447
|
-
const faceRef =
|
|
5448
|
-
const dragging =
|
|
5552
|
+
const [mode2, setMode] = React36.useState("h");
|
|
5553
|
+
const faceRef = React36.useRef(null);
|
|
5554
|
+
const dragging = React36.useRef(false);
|
|
5449
5555
|
const set = (h, mm, isPm) => onChange((isPm ? h % 12 + 12 : h % 12) + ":" + pad(mm));
|
|
5450
5556
|
const R = 108, NR = 80;
|
|
5451
5557
|
const nums = mode2 === "h" ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 12 }, (_, i) => i * 5);
|
|
@@ -5518,9 +5624,9 @@ function ClockFace({ value = "09:00", onChange }) {
|
|
|
5518
5624
|
}
|
|
5519
5625
|
function TimePicker({ label, help, error, required = false, disabled = false, value = "", onChange, placeholder = "Pick a time", id, className = "", style }) {
|
|
5520
5626
|
const { fieldId, labelId, ariaLabelledBy } = useTriggerLabelling(label, id);
|
|
5521
|
-
const [open, setOpen] =
|
|
5522
|
-
const rootRef =
|
|
5523
|
-
const boxRef =
|
|
5627
|
+
const [open, setOpen] = React36.useState(false);
|
|
5628
|
+
const rootRef = React36.useRef(null);
|
|
5629
|
+
const boxRef = React36.useRef(null);
|
|
5524
5630
|
const disp = () => {
|
|
5525
5631
|
if (!value) return "";
|
|
5526
5632
|
const [h, m] = value.split(":").map(Number);
|
|
@@ -5563,7 +5669,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
|
|
|
5563
5669
|
offset: 6,
|
|
5564
5670
|
width: 262,
|
|
5565
5671
|
role: "presentation",
|
|
5566
|
-
footer: /* @__PURE__ */ jsxs57(
|
|
5672
|
+
footer: /* @__PURE__ */ jsxs57(React36.Fragment, { children: [
|
|
5567
5673
|
/* @__PURE__ */ jsx63(
|
|
5568
5674
|
"button",
|
|
5569
5675
|
{
|
|
@@ -5591,7 +5697,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
|
|
|
5591
5697
|
}
|
|
5592
5698
|
|
|
5593
5699
|
// src/components/forms/Slider.tsx
|
|
5594
|
-
import * as
|
|
5700
|
+
import * as React37 from "react";
|
|
5595
5701
|
import { jsx as jsx64, jsxs as jsxs58 } from "react/jsx-runtime";
|
|
5596
5702
|
function Slider({
|
|
5597
5703
|
label,
|
|
@@ -5609,7 +5715,7 @@ function Slider({
|
|
|
5609
5715
|
...rest
|
|
5610
5716
|
}) {
|
|
5611
5717
|
const fieldId = useFieldId(id);
|
|
5612
|
-
const [dragging, setDragging] =
|
|
5718
|
+
const [dragging, setDragging] = React37.useState(false);
|
|
5613
5719
|
const v = value === void 0 ? min : Number(value);
|
|
5614
5720
|
const pct = max === min ? 0 : (v - min) / (max - min) * 100;
|
|
5615
5721
|
return /* @__PURE__ */ jsxs58("div", { className: ["fd-field", className].filter(Boolean).join(" "), children: [
|
|
@@ -5643,7 +5749,7 @@ function Slider({
|
|
|
5643
5749
|
}
|
|
5644
5750
|
|
|
5645
5751
|
// src/components/forms/RangeSlider.tsx
|
|
5646
|
-
import * as
|
|
5752
|
+
import * as React38 from "react";
|
|
5647
5753
|
import { jsx as jsx65, jsxs as jsxs59 } from "react/jsx-runtime";
|
|
5648
5754
|
function RangeSlider({
|
|
5649
5755
|
label,
|
|
@@ -5663,9 +5769,9 @@ function RangeSlider({
|
|
|
5663
5769
|
}) {
|
|
5664
5770
|
const fmt2 = format || ((v) => String(v));
|
|
5665
5771
|
const [a, b] = value || [min, max];
|
|
5666
|
-
const [drag, setDrag] =
|
|
5667
|
-
const [focus, setFocus] =
|
|
5668
|
-
const railRef =
|
|
5772
|
+
const [drag, setDrag] = React38.useState(null);
|
|
5773
|
+
const [focus, setFocus] = React38.useState(null);
|
|
5774
|
+
const railRef = React38.useRef(null);
|
|
5669
5775
|
const pct = (v) => max === min ? 0 : (v - min) / (max - min) * 100;
|
|
5670
5776
|
const clampPair = (i, v) => {
|
|
5671
5777
|
v = Math.min(max, Math.max(min, Math.round(v / step) * step));
|
|
@@ -5680,7 +5786,7 @@ function RangeSlider({
|
|
|
5680
5786
|
const r = railRef.current.getBoundingClientRect();
|
|
5681
5787
|
return min + Math.min(1, Math.max(0, (e.clientX - r.left) / r.width)) * (max - min);
|
|
5682
5788
|
};
|
|
5683
|
-
|
|
5789
|
+
React38.useEffect(() => {
|
|
5684
5790
|
if (drag === null) return;
|
|
5685
5791
|
const mv = (e) => onChange && onChange(clampPair(drag, fromEvent(e)));
|
|
5686
5792
|
const upH = () => setDrag(null);
|
|
@@ -5761,7 +5867,7 @@ function RangeSlider({
|
|
|
5761
5867
|
/* @__PURE__ */ jsxs59("div", { className: "fd-range" + (hasLabels ? " has-labels" : ""), onPointerDown: onRailDown, children: [
|
|
5762
5868
|
/* @__PURE__ */ jsx65("span", { className: "fd-range-rail", ref: railRef }),
|
|
5763
5869
|
/* @__PURE__ */ jsx65("span", { className: "fd-range-fill", style: { left: pct(a) + "%", width: pct(b) - pct(a) + "%" } }),
|
|
5764
|
-
marks.map((m) => /* @__PURE__ */ jsxs59(
|
|
5870
|
+
marks.map((m) => /* @__PURE__ */ jsxs59(React38.Fragment, { children: [
|
|
5765
5871
|
/* @__PURE__ */ jsx65("span", { className: "fd-range-mark" + (m.value >= a && m.value <= b ? " is-in" : ""), style: { left: pct(m.value) + "%" } }),
|
|
5766
5872
|
m.label ? /* @__PURE__ */ jsx65("span", { className: "fd-range-mark-label", style: { left: pct(m.value) + "%" }, children: m.label }) : null
|
|
5767
5873
|
] }, m.value)),
|
|
@@ -5790,7 +5896,7 @@ function RangeSlider({
|
|
|
5790
5896
|
}
|
|
5791
5897
|
|
|
5792
5898
|
// src/components/forms/Dropzone.tsx
|
|
5793
|
-
import * as
|
|
5899
|
+
import * as React39 from "react";
|
|
5794
5900
|
import { jsx as jsx66, jsxs as jsxs60 } from "react/jsx-runtime";
|
|
5795
5901
|
function Dropzone({
|
|
5796
5902
|
onFiles,
|
|
@@ -5805,8 +5911,8 @@ function Dropzone({
|
|
|
5805
5911
|
className = "",
|
|
5806
5912
|
style
|
|
5807
5913
|
}) {
|
|
5808
|
-
const [over, setOver] =
|
|
5809
|
-
const depth =
|
|
5914
|
+
const [over, setOver] = React39.useState(false);
|
|
5915
|
+
const depth = React39.useRef(0);
|
|
5810
5916
|
const has = (e) => {
|
|
5811
5917
|
const dt = e.dataTransfer;
|
|
5812
5918
|
if (!dt) return false;
|
|
@@ -5868,8 +5974,8 @@ function FilePickButton({
|
|
|
5868
5974
|
className = "",
|
|
5869
5975
|
children
|
|
5870
5976
|
}) {
|
|
5871
|
-
const ref =
|
|
5872
|
-
return /* @__PURE__ */ jsxs60(
|
|
5977
|
+
const ref = React39.useRef(null);
|
|
5978
|
+
return /* @__PURE__ */ jsxs60(React39.Fragment, { children: [
|
|
5873
5979
|
/* @__PURE__ */ jsx66(
|
|
5874
5980
|
"button",
|
|
5875
5981
|
{
|
|
@@ -5902,10 +6008,10 @@ function FilePickButton({
|
|
|
5902
6008
|
}
|
|
5903
6009
|
function useStagedFiles(upload, opts) {
|
|
5904
6010
|
const o = opts || {};
|
|
5905
|
-
const [items, setItems] =
|
|
5906
|
-
const controllers =
|
|
6011
|
+
const [items, setItems] = React39.useState([]);
|
|
6012
|
+
const controllers = React39.useRef({});
|
|
5907
6013
|
const patch = (id, next) => setItems((list) => list.map((f) => f.id === id ? Object.assign({}, f, next) : f));
|
|
5908
|
-
const run =
|
|
6014
|
+
const run = React39.useCallback((att) => {
|
|
5909
6015
|
if (!upload) return;
|
|
5910
6016
|
const ac = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
5911
6017
|
controllers.current[att.id] = ac;
|
|
@@ -5922,14 +6028,14 @@ function useStagedFiles(upload, opts) {
|
|
|
5922
6028
|
delete controllers.current[att.id];
|
|
5923
6029
|
});
|
|
5924
6030
|
}, [upload]);
|
|
5925
|
-
const add =
|
|
6031
|
+
const add = React39.useCallback((files) => {
|
|
5926
6032
|
if (!upload) return [];
|
|
5927
6033
|
const atts = Array.from(files).map((f) => toAttachment(f));
|
|
5928
6034
|
setItems((list) => list.concat(atts));
|
|
5929
6035
|
atts.forEach(run);
|
|
5930
6036
|
return atts;
|
|
5931
6037
|
}, [upload, run]);
|
|
5932
|
-
const remove =
|
|
6038
|
+
const remove = React39.useCallback((att) => {
|
|
5933
6039
|
const ac = controllers.current[att.id];
|
|
5934
6040
|
if (ac) {
|
|
5935
6041
|
try {
|
|
@@ -5940,10 +6046,10 @@ function useStagedFiles(upload, opts) {
|
|
|
5940
6046
|
}
|
|
5941
6047
|
setItems((list) => list.filter((f) => f.id !== att.id));
|
|
5942
6048
|
}, []);
|
|
5943
|
-
const retry =
|
|
6049
|
+
const retry = React39.useCallback((att) => {
|
|
5944
6050
|
run(att);
|
|
5945
6051
|
}, [run]);
|
|
5946
|
-
const clear =
|
|
6052
|
+
const clear = React39.useCallback(() => {
|
|
5947
6053
|
Object.values(controllers.current).forEach((ac) => {
|
|
5948
6054
|
try {
|
|
5949
6055
|
ac && ac.abort();
|
|
@@ -6067,7 +6173,7 @@ function FileGrid({ files = [], onOpen, onRemove, onRetry, tiles = true, maxHeig
|
|
|
6067
6173
|
}
|
|
6068
6174
|
|
|
6069
6175
|
// src/components/forms/MarkdownEditor.tsx
|
|
6070
|
-
import * as
|
|
6176
|
+
import * as React40 from "react";
|
|
6071
6177
|
import { jsx as jsx68, jsxs as jsxs62 } from "react/jsx-runtime";
|
|
6072
6178
|
var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent || "");
|
|
6073
6179
|
var INLINE_RE = /(\*\*\*[^*\n]+\*\*\*|\*\*[^*\n]+\*\*|__[^_\n]+__|~~[^~\n]+~~|`[^`\n]+`|\*[^*\s][^*\n]*\*|(?<![A-Za-z0-9_])_[^_\s][^_\n]*_|\[[^\]\n]*\]\([^)\s\n]*\)|https?:\/\/\S+)/g;
|
|
@@ -6281,7 +6387,7 @@ function syncDom(root, value) {
|
|
|
6281
6387
|
while (root.children.length > lines.length) root.removeChild(root.lastChild);
|
|
6282
6388
|
}
|
|
6283
6389
|
var LIST_CONT = RE_LI;
|
|
6284
|
-
var MarkdownEditor =
|
|
6390
|
+
var MarkdownEditor = React40.forwardRef(function MarkdownEditor2({
|
|
6285
6391
|
value = "",
|
|
6286
6392
|
onChange,
|
|
6287
6393
|
onSubmit,
|
|
@@ -6300,10 +6406,10 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
|
|
|
6300
6406
|
className = "",
|
|
6301
6407
|
id
|
|
6302
6408
|
}, ref) {
|
|
6303
|
-
const boxRef =
|
|
6304
|
-
const composing =
|
|
6305
|
-
const pendingCaret =
|
|
6306
|
-
|
|
6409
|
+
const boxRef = React40.useRef(null);
|
|
6410
|
+
const composing = React40.useRef(false);
|
|
6411
|
+
const pendingCaret = React40.useRef(null);
|
|
6412
|
+
React40.useLayoutEffect(() => {
|
|
6307
6413
|
const root = boxRef.current;
|
|
6308
6414
|
if (!root || composing.current) return;
|
|
6309
6415
|
const active = document.activeElement === root || root.contains(document.activeElement);
|
|
@@ -6312,7 +6418,7 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
|
|
|
6312
6418
|
pendingCaret.current = null;
|
|
6313
6419
|
if (active && caret != null) placeCaret(root, caret);
|
|
6314
6420
|
}, [value]);
|
|
6315
|
-
|
|
6421
|
+
React40.useEffect(() => {
|
|
6316
6422
|
if (autoFocus && boxRef.current) boxRef.current.focus();
|
|
6317
6423
|
}, [autoFocus]);
|
|
6318
6424
|
const caretNow = () => {
|
|
@@ -6379,7 +6485,7 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
|
|
|
6379
6485
|
api.replaceRange(from, to, next, from + next.length);
|
|
6380
6486
|
}
|
|
6381
6487
|
};
|
|
6382
|
-
|
|
6488
|
+
React40.useImperativeHandle(ref, () => api);
|
|
6383
6489
|
function detect(text, caret) {
|
|
6384
6490
|
if (!onTrigger) return;
|
|
6385
6491
|
const upto = text.slice(0, caret);
|
|
@@ -6531,16 +6637,16 @@ var MarkdownEditor = React39.forwardRef(function MarkdownEditor2({
|
|
|
6531
6637
|
});
|
|
6532
6638
|
|
|
6533
6639
|
// src/components/platform/AccountMenu.tsx
|
|
6534
|
-
import * as
|
|
6640
|
+
import * as React42 from "react";
|
|
6535
6641
|
|
|
6536
6642
|
// src/kits/sessionContext.tsx
|
|
6537
|
-
import * as
|
|
6538
|
-
var SessionContext =
|
|
6643
|
+
import * as React41 from "react";
|
|
6644
|
+
var SessionContext = React41.createContext(null);
|
|
6539
6645
|
function SessionProvider({ value, children }) {
|
|
6540
|
-
return
|
|
6646
|
+
return React41.createElement(SessionContext.Provider, { value }, children);
|
|
6541
6647
|
}
|
|
6542
6648
|
function useSessionContext() {
|
|
6543
|
-
const ctx =
|
|
6649
|
+
const ctx = React41.useContext(SessionContext);
|
|
6544
6650
|
if (!ctx) {
|
|
6545
6651
|
throw new Error(
|
|
6546
6652
|
"useSessionContext() was called outside a <SessionProvider>. Wrap your app root in <SessionProvider value={...}> with your real session, or \u2014 in the docs site or a demo \u2014 in <DemoSessionProvider>, before rendering AccountMenu, Gate, ProfilePage or AppSwitcher."
|
|
@@ -6580,11 +6686,11 @@ function AccountMenu({
|
|
|
6580
6686
|
...rest
|
|
6581
6687
|
}) {
|
|
6582
6688
|
const { user, roles, can: can2, signOut: ctxSignOut, demo } = useSessionContext();
|
|
6583
|
-
const [open, setOpen] =
|
|
6584
|
-
const [switching, setSwitching] =
|
|
6585
|
-
const [signingOut, setSigningOut] =
|
|
6586
|
-
const [signOutError, setSignOutError] =
|
|
6587
|
-
const ref =
|
|
6689
|
+
const [open, setOpen] = React42.useState(false);
|
|
6690
|
+
const [switching, setSwitching] = React42.useState(false);
|
|
6691
|
+
const [signingOut, setSigningOut] = React42.useState(false);
|
|
6692
|
+
const [signOutError, setSignOutError] = React42.useState(null);
|
|
6693
|
+
const ref = React42.useRef(null);
|
|
6588
6694
|
const visibleLinks = links.filter((l) => !l.perm || can2(l.perm));
|
|
6589
6695
|
const visibleGroups = groups.map((g) => ({ label: g.label, links: g.links.filter((l) => !l.perm || can2(l.perm)) })).filter((g) => g.links.length);
|
|
6590
6696
|
const closeMenu = () => {
|
|
@@ -6709,13 +6815,13 @@ function AccountMenu({
|
|
|
6709
6815
|
}
|
|
6710
6816
|
|
|
6711
6817
|
// src/components/platform/AppSwitcher.tsx
|
|
6712
|
-
import * as
|
|
6818
|
+
import * as React43 from "react";
|
|
6713
6819
|
import { jsx as jsx70, jsxs as jsxs64 } from "react/jsx-runtime";
|
|
6714
6820
|
function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
|
|
6715
6821
|
const { apps: contextApps } = useSessionContext();
|
|
6716
6822
|
const apps = appsProp ?? contextApps ?? [];
|
|
6717
|
-
const [open, setOpen] =
|
|
6718
|
-
const anchorRef =
|
|
6823
|
+
const [open, setOpen] = React43.useState(false);
|
|
6824
|
+
const anchorRef = React43.useRef(null);
|
|
6719
6825
|
if (!apps.length) return null;
|
|
6720
6826
|
const items = apps.map((app) => ({
|
|
6721
6827
|
id: app.key,
|
|
@@ -6729,7 +6835,7 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
|
|
|
6729
6835
|
window.location.href = app.url;
|
|
6730
6836
|
}
|
|
6731
6837
|
}));
|
|
6732
|
-
return /* @__PURE__ */ jsxs64(
|
|
6838
|
+
return /* @__PURE__ */ jsxs64(React43.Fragment, { children: [
|
|
6733
6839
|
/* @__PURE__ */ jsx70("span", { ref: anchorRef, className: ["fd-appswitch-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx70(
|
|
6734
6840
|
IconButton,
|
|
6735
6841
|
{
|
|
@@ -6758,10 +6864,10 @@ function AppSwitcher({ apps: appsProp, onNavigate, className = "" }) {
|
|
|
6758
6864
|
}
|
|
6759
6865
|
|
|
6760
6866
|
// src/components/platform/AppearanceMenu.tsx
|
|
6761
|
-
import * as
|
|
6867
|
+
import * as React45 from "react";
|
|
6762
6868
|
|
|
6763
6869
|
// src/kits/appearance.ts
|
|
6764
|
-
import * as
|
|
6870
|
+
import * as React44 from "react";
|
|
6765
6871
|
var DEFAULT_STORAGE_KEY = "fd-appearance";
|
|
6766
6872
|
var DEFAULT_PREFS = {
|
|
6767
6873
|
theme: "system",
|
|
@@ -6856,11 +6962,11 @@ function applyPrefs(prefs, root = document.documentElement) {
|
|
|
6856
6962
|
else root.removeAttribute("data-motion");
|
|
6857
6963
|
}
|
|
6858
6964
|
function useAppearancePrefs(store) {
|
|
6859
|
-
const prefs =
|
|
6860
|
-
|
|
6965
|
+
const prefs = React44.useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
6966
|
+
React44.useEffect(() => {
|
|
6861
6967
|
applyPrefs(prefs);
|
|
6862
6968
|
}, [prefs]);
|
|
6863
|
-
|
|
6969
|
+
React44.useEffect(() => {
|
|
6864
6970
|
if (prefs.theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
6865
6971
|
return;
|
|
6866
6972
|
}
|
|
@@ -6869,7 +6975,7 @@ function useAppearancePrefs(store) {
|
|
|
6869
6975
|
mq.addEventListener("change", onChange);
|
|
6870
6976
|
return () => mq.removeEventListener("change", onChange);
|
|
6871
6977
|
}, [prefs]);
|
|
6872
|
-
const update =
|
|
6978
|
+
const update = React44.useCallback(
|
|
6873
6979
|
(patch) => {
|
|
6874
6980
|
store.write({ ...store.getSnapshot(), ...patch });
|
|
6875
6981
|
},
|
|
@@ -6892,15 +6998,15 @@ var TEXT_SCALE_OPTIONS = [
|
|
|
6892
6998
|
{ value: "xl", label: "XL" }
|
|
6893
6999
|
];
|
|
6894
7000
|
function AppearanceMenu({ className = "", store: storeProp, storageKey, storage, defaults }) {
|
|
6895
|
-
const store =
|
|
7001
|
+
const store = React45.useMemo(
|
|
6896
7002
|
() => storeProp ?? createLocalStorageAppearanceStore({ storageKey, storage, defaults }),
|
|
6897
7003
|
[storeProp, storageKey, storage, defaults]
|
|
6898
7004
|
);
|
|
6899
7005
|
const [prefs, update] = useAppearancePrefs(store);
|
|
6900
|
-
const [open, setOpen] =
|
|
6901
|
-
const anchorRef =
|
|
7006
|
+
const [open, setOpen] = React45.useState(false);
|
|
7007
|
+
const anchorRef = React45.useRef(null);
|
|
6902
7008
|
const patch = (p) => update(p);
|
|
6903
|
-
return /* @__PURE__ */ jsxs65(
|
|
7009
|
+
return /* @__PURE__ */ jsxs65(React45.Fragment, { children: [
|
|
6904
7010
|
/* @__PURE__ */ jsx71("span", { ref: anchorRef, className: ["fd-appearance-anchor", className].filter(Boolean).join(" "), style: { display: "inline-flex" }, children: /* @__PURE__ */ jsx71(
|
|
6905
7011
|
IconButton,
|
|
6906
7012
|
{
|
|
@@ -6969,14 +7075,14 @@ function AppearanceMenu({ className = "", store: storeProp, storageKey, storage,
|
|
|
6969
7075
|
}
|
|
6970
7076
|
|
|
6971
7077
|
// src/components/platform/ApiSpecBrowser.tsx
|
|
6972
|
-
import * as
|
|
7078
|
+
import * as React46 from "react";
|
|
6973
7079
|
import { jsx as jsx72, jsxs as jsxs66 } from "react/jsx-runtime";
|
|
6974
7080
|
var METHOD_TONE = { GET: "success", POST: "info", PATCH: "warning", PUT: "warning", DELETE: "danger" };
|
|
6975
7081
|
function Json({ obj }) {
|
|
6976
7082
|
return /* @__PURE__ */ jsx72("pre", { className: "fd-json", children: JSON.stringify(obj, null, 2) });
|
|
6977
7083
|
}
|
|
6978
7084
|
function Endpoint({ s, onRequest }) {
|
|
6979
|
-
const [tried, setTried] =
|
|
7085
|
+
const [tried, setTried] = React46.useState(null);
|
|
6980
7086
|
const run = async () => {
|
|
6981
7087
|
setTried("busy");
|
|
6982
7088
|
const t0 = (window.performance || Date).now();
|
|
@@ -7049,10 +7155,10 @@ function ApiSpecBrowser({
|
|
|
7049
7155
|
className = "",
|
|
7050
7156
|
...rest
|
|
7051
7157
|
}) {
|
|
7052
|
-
const [q, setQ] =
|
|
7053
|
-
const [method, setMethod] =
|
|
7054
|
-
const [mod, setMod] =
|
|
7055
|
-
const [listOnly, setListOnly] =
|
|
7158
|
+
const [q, setQ] = React46.useState("");
|
|
7159
|
+
const [method, setMethod] = React46.useState(null);
|
|
7160
|
+
const [mod, setMod] = React46.useState(null);
|
|
7161
|
+
const [listOnly, setListOnly] = React46.useState(false);
|
|
7056
7162
|
const activeModule = modules && modules.find((m) => m.id === mod);
|
|
7057
7163
|
const hits = spec.filter((s) => (!method || s.method === method) && (!listOnly || s.isList) && (!activeModule || activeModule.endpoints.indexOf(s.id) >= 0) && (!q || (s.path + " " + s.title + " " + s.purpose + " " + (s.usedBy || []).join(" ")).toLowerCase().includes(q.toLowerCase())));
|
|
7058
7164
|
const effGroups = groups && groups.length ? groups : [["All endpoints", spec.map((s) => s.id)]];
|
|
@@ -7131,7 +7237,7 @@ function ApiSpecBrowser({
|
|
|
7131
7237
|
}
|
|
7132
7238
|
|
|
7133
7239
|
// src/components/platform/ProfilePage.tsx
|
|
7134
|
-
import * as
|
|
7240
|
+
import * as React47 from "react";
|
|
7135
7241
|
import { jsx as jsx73, jsxs as jsxs67 } from "react/jsx-runtime";
|
|
7136
7242
|
function fieldInitialValue(field, user) {
|
|
7137
7243
|
if (field.value) return field.value(user);
|
|
@@ -7167,21 +7273,21 @@ function ProfilePage({
|
|
|
7167
7273
|
}) {
|
|
7168
7274
|
const { user: sessionUser, roles, permissions, saveProfile: saveProfile2 } = useSessionContext();
|
|
7169
7275
|
const user = userProp || sessionUser;
|
|
7170
|
-
const initialFieldValues =
|
|
7276
|
+
const initialFieldValues = React47.useMemo(() => {
|
|
7171
7277
|
const values2 = {};
|
|
7172
7278
|
for (const field of fields) values2[field.key] = fieldInitialValue(field, user);
|
|
7173
7279
|
return values2;
|
|
7174
7280
|
}, [user.id, fields]);
|
|
7175
|
-
const initialNotify =
|
|
7281
|
+
const initialNotify = React47.useMemo(() => {
|
|
7176
7282
|
const values2 = {};
|
|
7177
7283
|
for (const n of notifications) values2[n.key] = !!user.notify?.[n.key];
|
|
7178
7284
|
return values2;
|
|
7179
7285
|
}, [user.id, notifications]);
|
|
7180
|
-
const [values, setValues] =
|
|
7181
|
-
const [notify, setNotify] =
|
|
7182
|
-
const [saving, setSaving] =
|
|
7183
|
-
const [saved, setSaved] =
|
|
7184
|
-
const [prevUserId, setPrevUserId] =
|
|
7286
|
+
const [values, setValues] = React47.useState(initialFieldValues);
|
|
7287
|
+
const [notify, setNotify] = React47.useState(initialNotify);
|
|
7288
|
+
const [saving, setSaving] = React47.useState(false);
|
|
7289
|
+
const [saved, setSaved] = React47.useState(false);
|
|
7290
|
+
const [prevUserId, setPrevUserId] = React47.useState(user.id);
|
|
7185
7291
|
if (user.id !== prevUserId) {
|
|
7186
7292
|
setPrevUserId(user.id);
|
|
7187
7293
|
setValues(initialFieldValues);
|
|
@@ -7369,11 +7475,11 @@ function ProfilePage({
|
|
|
7369
7475
|
}
|
|
7370
7476
|
|
|
7371
7477
|
// src/components/platform/SignInPage.tsx
|
|
7372
|
-
import * as
|
|
7478
|
+
import * as React48 from "react";
|
|
7373
7479
|
import { Fragment as Fragment18, jsx as jsx74, jsxs as jsxs68 } from "react/jsx-runtime";
|
|
7374
7480
|
function SignInPage({ productName, onSignIn, continueAs, error }) {
|
|
7375
|
-
const [signingIn, setSigningIn] =
|
|
7376
|
-
const [resuming, setResuming] =
|
|
7481
|
+
const [signingIn, setSigningIn] = React48.useState(false);
|
|
7482
|
+
const [resuming, setResuming] = React48.useState(false);
|
|
7377
7483
|
function handleSignIn() {
|
|
7378
7484
|
setSigningIn(true);
|
|
7379
7485
|
onSignIn();
|
|
@@ -7562,10 +7668,10 @@ function GoogleGlyph() {
|
|
|
7562
7668
|
}
|
|
7563
7669
|
|
|
7564
7670
|
// src/components/platform/RoadmapTimeline.tsx
|
|
7565
|
-
import * as
|
|
7671
|
+
import * as React51 from "react";
|
|
7566
7672
|
|
|
7567
7673
|
// src/kits/session.ts
|
|
7568
|
-
import * as
|
|
7674
|
+
import * as React49 from "react";
|
|
7569
7675
|
var PERMISSION_CATALOG = [
|
|
7570
7676
|
{ group: "Plans", items: [
|
|
7571
7677
|
{ key: "plan.view", label: "View plans", detail: "Read any plan in the workspace." },
|
|
@@ -9132,8 +9238,8 @@ function roadmap(overrides) {
|
|
|
9132
9238
|
};
|
|
9133
9239
|
}
|
|
9134
9240
|
function useSession() {
|
|
9135
|
-
const [s, setS] =
|
|
9136
|
-
|
|
9241
|
+
const [s, setS] = React49.useState(getSession);
|
|
9242
|
+
React49.useEffect(() => subscribe(setS), []);
|
|
9137
9243
|
return s;
|
|
9138
9244
|
}
|
|
9139
9245
|
var SessionKit = {
|
|
@@ -9166,7 +9272,7 @@ var SessionKit = {
|
|
|
9166
9272
|
};
|
|
9167
9273
|
function DemoSessionProvider({ children }) {
|
|
9168
9274
|
const session = useSession();
|
|
9169
|
-
const value =
|
|
9275
|
+
const value = React49.useMemo(
|
|
9170
9276
|
() => ({
|
|
9171
9277
|
user: session.user,
|
|
9172
9278
|
roles: session.roles,
|
|
@@ -9182,11 +9288,11 @@ function DemoSessionProvider({ children }) {
|
|
|
9182
9288
|
}),
|
|
9183
9289
|
[session]
|
|
9184
9290
|
);
|
|
9185
|
-
return
|
|
9291
|
+
return React49.createElement(SessionProvider, { value }, children);
|
|
9186
9292
|
}
|
|
9187
9293
|
|
|
9188
9294
|
// src/kits/runtime.ts
|
|
9189
|
-
import * as
|
|
9295
|
+
import * as React50 from "react";
|
|
9190
9296
|
var WIRED_ENDPOINTS = [
|
|
9191
9297
|
// Nothing yet. Every id below would come from a real service:
|
|
9192
9298
|
// "plan.get", "placements.list", …
|
|
@@ -9357,8 +9463,8 @@ var RuntimeKit = {
|
|
|
9357
9463
|
};
|
|
9358
9464
|
RuntimeKit.declare(FEATURE_NEEDS);
|
|
9359
9465
|
function useRuntimeMode() {
|
|
9360
|
-
const [m, setM] =
|
|
9361
|
-
|
|
9466
|
+
const [m, setM] = React50.useState(RuntimeKit.getMode());
|
|
9467
|
+
React50.useEffect(() => RuntimeKit.subscribe(setM), []);
|
|
9362
9468
|
return m;
|
|
9363
9469
|
}
|
|
9364
9470
|
function useFeatureStatus(key) {
|
|
@@ -9441,12 +9547,12 @@ function RoadmapCard({ item, byKey, onOpenFlag }) {
|
|
|
9441
9547
|
}
|
|
9442
9548
|
function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
9443
9549
|
const session = SessionKit.useSession();
|
|
9444
|
-
const [project, setProject] =
|
|
9445
|
-
const [q, setQ] =
|
|
9446
|
-
const scrollRef =
|
|
9447
|
-
const nowRef =
|
|
9448
|
-
const rm =
|
|
9449
|
-
const byKey =
|
|
9550
|
+
const [project, setProject] = React51.useState("");
|
|
9551
|
+
const [q, setQ] = React51.useState("");
|
|
9552
|
+
const scrollRef = React51.useRef(null);
|
|
9553
|
+
const nowRef = React51.useRef(null);
|
|
9554
|
+
const rm = React51.useMemo(() => SessionKit.roadmap({ isComplete: RuntimeKit.isComplete }), [session]);
|
|
9555
|
+
const byKey = React51.useMemo(() => {
|
|
9450
9556
|
const m = {};
|
|
9451
9557
|
rm.items.forEach((i) => {
|
|
9452
9558
|
m[i.key] = i;
|
|
@@ -9455,7 +9561,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
9455
9561
|
}, [rm]);
|
|
9456
9562
|
const items = rm.items.filter((i) => (!project || i.project === project) && (!q || (i.label + " " + i.description + " " + (i.backend || "") + " " + i.key).toLowerCase().includes(q.toLowerCase())));
|
|
9457
9563
|
const projects = [...new Set(rm.items.map((i) => i.project))];
|
|
9458
|
-
|
|
9564
|
+
React51.useEffect(() => {
|
|
9459
9565
|
let raf1 = 0, raf2 = 0;
|
|
9460
9566
|
const place = () => {
|
|
9461
9567
|
const box = scrollRef.current, mark = nowRef.current;
|
|
@@ -9529,7 +9635,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
9529
9635
|
lastPhase = item.phase;
|
|
9530
9636
|
const inPhase = items.filter((i) => i.phase === item.phase).length;
|
|
9531
9637
|
const isBoundary = n === firstPending;
|
|
9532
|
-
return /* @__PURE__ */ jsxs69(
|
|
9638
|
+
return /* @__PURE__ */ jsxs69(React51.Fragment, { children: [
|
|
9533
9639
|
showPhase ? /* @__PURE__ */ jsxs69("div", { className: "fd-rm-era", children: [
|
|
9534
9640
|
/* @__PURE__ */ jsxs69("span", { className: "fd-row", style: { gap: 8, flexWrap: "wrap", alignItems: "baseline" }, children: [
|
|
9535
9641
|
/* @__PURE__ */ jsx75("span", { style: { fontWeight: 700 }, children: item.phase === 0 ? "Shipped" : "Phase " + item.phase + " \u2014 " + item.phaseName }),
|
|
@@ -9573,7 +9679,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
|
|
|
9573
9679
|
}
|
|
9574
9680
|
|
|
9575
9681
|
// src/components/platform/ComingSoon.tsx
|
|
9576
|
-
import * as
|
|
9682
|
+
import * as React52 from "react";
|
|
9577
9683
|
import { createPortal as createPortal5 } from "react-dom";
|
|
9578
9684
|
import { Fragment as Fragment20, jsx as jsx76, jsxs as jsxs70 } from "react/jsx-runtime";
|
|
9579
9685
|
var BYPASS_STORE = "fd.soon.bypass.v1";
|
|
@@ -9591,8 +9697,8 @@ function writeBypassed(list) {
|
|
|
9591
9697
|
}
|
|
9592
9698
|
}
|
|
9593
9699
|
function useBypass(key) {
|
|
9594
|
-
const [on, setOn] =
|
|
9595
|
-
|
|
9700
|
+
const [on, setOn] = React52.useState(() => !!key && readBypassed().indexOf(key) >= 0);
|
|
9701
|
+
React52.useEffect(() => {
|
|
9596
9702
|
setOn(!!key && readBypassed().indexOf(key) >= 0);
|
|
9597
9703
|
}, [key]);
|
|
9598
9704
|
const set = (next) => {
|
|
@@ -9639,9 +9745,9 @@ function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onB
|
|
|
9639
9745
|
] });
|
|
9640
9746
|
}
|
|
9641
9747
|
function useHoverCard(open) {
|
|
9642
|
-
const anchor =
|
|
9643
|
-
const [pos, setPos] =
|
|
9644
|
-
|
|
9748
|
+
const anchor = React52.useRef(null);
|
|
9749
|
+
const [pos, setPos] = React52.useState(null);
|
|
9750
|
+
React52.useLayoutEffect(() => {
|
|
9645
9751
|
if (!open || !anchor.current) {
|
|
9646
9752
|
setPos(null);
|
|
9647
9753
|
return;
|
|
@@ -9768,10 +9874,10 @@ function ComingSoon({
|
|
|
9768
9874
|
] });
|
|
9769
9875
|
}
|
|
9770
9876
|
function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass, blur, tip, className, rest, children }) {
|
|
9771
|
-
const [open, setOpen] =
|
|
9877
|
+
const [open, setOpen] = React52.useState(false);
|
|
9772
9878
|
const [anchor, pos] = useHoverCard(open);
|
|
9773
9879
|
const zIndex = useOverlayLayer(open);
|
|
9774
|
-
const close =
|
|
9880
|
+
const close = React52.useRef(null);
|
|
9775
9881
|
const show = () => {
|
|
9776
9882
|
if (close.current) {
|
|
9777
9883
|
clearTimeout(close.current);
|
|
@@ -9787,7 +9893,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
|
|
|
9787
9893
|
setOpen(false);
|
|
9788
9894
|
}, 140);
|
|
9789
9895
|
};
|
|
9790
|
-
|
|
9896
|
+
React52.useEffect(() => () => {
|
|
9791
9897
|
if (close.current) clearTimeout(close.current);
|
|
9792
9898
|
}, []);
|
|
9793
9899
|
return /* @__PURE__ */ jsxs70(
|
|
@@ -9958,14 +10064,14 @@ function PermissionHint({ perm, children }) {
|
|
|
9958
10064
|
}
|
|
9959
10065
|
|
|
9960
10066
|
// src/components/platform/ModeSwitch.tsx
|
|
9961
|
-
import * as
|
|
10067
|
+
import * as React53 from "react";
|
|
9962
10068
|
import { Fragment as Fragment21, jsx as jsx78, jsxs as jsxs72 } from "react/jsx-runtime";
|
|
9963
10069
|
function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
|
|
9964
10070
|
const mode2 = useRuntimeMode();
|
|
9965
|
-
const [open, setOpen] =
|
|
10071
|
+
const [open, setOpen] = React53.useState(false);
|
|
9966
10072
|
const zIndex = useOverlayLayer(open);
|
|
9967
|
-
const ref =
|
|
9968
|
-
|
|
10073
|
+
const ref = React53.useRef(null);
|
|
10074
|
+
React53.useEffect(() => {
|
|
9969
10075
|
if (!open) return;
|
|
9970
10076
|
const away = (e) => {
|
|
9971
10077
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -10221,7 +10327,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
|
|
|
10221
10327
|
}
|
|
10222
10328
|
|
|
10223
10329
|
// src/components/planner/MixGap.tsx
|
|
10224
|
-
import * as
|
|
10330
|
+
import * as React54 from "react";
|
|
10225
10331
|
import { Fragment as Fragment22, jsx as jsx81, jsxs as jsxs75 } from "react/jsx-runtime";
|
|
10226
10332
|
var MIX_GAP_LAYOUT = {
|
|
10227
10333
|
trackHeight: 30,
|
|
@@ -10236,7 +10342,7 @@ function mixGapOffset(value, max) {
|
|
|
10236
10342
|
}
|
|
10237
10343
|
function MixGap({ rows = [], loading = false, className = "" }) {
|
|
10238
10344
|
const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
|
|
10239
|
-
const [hover, setHover] =
|
|
10345
|
+
const [hover, setHover] = React54.useState(null);
|
|
10240
10346
|
const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
|
|
10241
10347
|
const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
|
|
10242
10348
|
const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
|
|
@@ -10379,7 +10485,7 @@ function ChannelContribution({
|
|
|
10379
10485
|
}
|
|
10380
10486
|
|
|
10381
10487
|
// src/components/planner/BudgetReallocator.tsx
|
|
10382
|
-
import * as
|
|
10488
|
+
import * as React55 from "react";
|
|
10383
10489
|
import { Fragment as Fragment23, jsx as jsx83, jsxs as jsxs77 } from "react/jsx-runtime";
|
|
10384
10490
|
var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
|
|
10385
10491
|
function BudgetReallocator({
|
|
@@ -10395,8 +10501,8 @@ function BudgetReallocator({
|
|
|
10395
10501
|
onCancel,
|
|
10396
10502
|
className = ""
|
|
10397
10503
|
}) {
|
|
10398
|
-
const [draft, setDraft] =
|
|
10399
|
-
|
|
10504
|
+
const [draft, setDraft] = React55.useState(spend);
|
|
10505
|
+
React55.useEffect(() => setDraft(spend), [spend]);
|
|
10400
10506
|
const dirty = draft !== spend;
|
|
10401
10507
|
const nextCrp = scoreFor ? scoreFor(draft) : crp;
|
|
10402
10508
|
const nextBand = bandFor(nextCrp);
|
|
@@ -10411,10 +10517,10 @@ function BudgetReallocator({
|
|
|
10411
10517
|
setDraft(spend);
|
|
10412
10518
|
if (onCancel) onCancel();
|
|
10413
10519
|
};
|
|
10414
|
-
const trackRef =
|
|
10415
|
-
const chipRef =
|
|
10416
|
-
const [chipLeft, setChipLeft] =
|
|
10417
|
-
|
|
10520
|
+
const trackRef = React55.useRef(null);
|
|
10521
|
+
const chipRef = React55.useRef(null);
|
|
10522
|
+
const [chipLeft, setChipLeft] = React55.useState(null);
|
|
10523
|
+
React55.useLayoutEffect(() => {
|
|
10418
10524
|
const track = trackRef.current;
|
|
10419
10525
|
if (!dirty || !track) {
|
|
10420
10526
|
setChipLeft(null);
|
|
@@ -10601,7 +10707,7 @@ function ImportanceTag({ level, showLabel = true, size = "sm", className = "" })
|
|
|
10601
10707
|
}
|
|
10602
10708
|
|
|
10603
10709
|
// src/components/planner/PreferenceMeter.tsx
|
|
10604
|
-
import * as
|
|
10710
|
+
import * as React56 from "react";
|
|
10605
10711
|
import { jsx as jsx86, jsxs as jsxs80 } from "react/jsx-runtime";
|
|
10606
10712
|
function preferenceScore(criteria) {
|
|
10607
10713
|
const earned = criteria.reduce((n, c) => n + (Number(c.earned) || 0), 0);
|
|
@@ -10631,7 +10737,7 @@ function PreferenceMeter({
|
|
|
10631
10737
|
className = ""
|
|
10632
10738
|
}) {
|
|
10633
10739
|
const value = score == null ? preferenceScore(criteria) : score;
|
|
10634
|
-
const segments =
|
|
10740
|
+
const segments = React56.useMemo(() => preferenceSegments(criteria), [criteria]);
|
|
10635
10741
|
return /* @__PURE__ */ jsx86(
|
|
10636
10742
|
ScoreMeter,
|
|
10637
10743
|
{
|
|
@@ -10670,10 +10776,10 @@ function PreferenceMeter({
|
|
|
10670
10776
|
}
|
|
10671
10777
|
|
|
10672
10778
|
// src/components/chat/AgentChatPanel.tsx
|
|
10673
|
-
import * as
|
|
10779
|
+
import * as React62 from "react";
|
|
10674
10780
|
|
|
10675
10781
|
// src/components/chat/chatEngine.ts
|
|
10676
|
-
import * as
|
|
10782
|
+
import * as React57 from "react";
|
|
10677
10783
|
var CHAT_UNAVAILABLE = "chat_unavailable";
|
|
10678
10784
|
var JOB_PENDING = ["queued", "running"];
|
|
10679
10785
|
var JOB_SUCCESS = ["completed", "recovered"];
|
|
@@ -10727,22 +10833,22 @@ function useChatEngine(opts) {
|
|
|
10727
10833
|
onClear,
|
|
10728
10834
|
onFeedback
|
|
10729
10835
|
} = opts || {};
|
|
10730
|
-
const [status, setStatus] =
|
|
10731
|
-
const [threadId, setThreadId] =
|
|
10732
|
-
const [messages, setMessages] =
|
|
10733
|
-
const [queue, setQueue] =
|
|
10734
|
-
const [fatal, setFatal] =
|
|
10735
|
-
const [busy, setBusy] =
|
|
10736
|
-
const [turnStartedAt, setTurnStartedAt] =
|
|
10737
|
-
const listRef =
|
|
10738
|
-
const queueRef =
|
|
10739
|
-
const busyRef =
|
|
10740
|
-
const stoppedRef =
|
|
10741
|
-
const abortRef =
|
|
10742
|
-
const serverCount =
|
|
10743
|
-
const threadRef =
|
|
10744
|
-
const mounted =
|
|
10745
|
-
|
|
10836
|
+
const [status, setStatus] = React57.useState("idle");
|
|
10837
|
+
const [threadId, setThreadId] = React57.useState(null);
|
|
10838
|
+
const [messages, setMessages] = React57.useState([]);
|
|
10839
|
+
const [queue, setQueue] = React57.useState([]);
|
|
10840
|
+
const [fatal, setFatal] = React57.useState(null);
|
|
10841
|
+
const [busy, setBusy] = React57.useState(false);
|
|
10842
|
+
const [turnStartedAt, setTurnStartedAt] = React57.useState(null);
|
|
10843
|
+
const listRef = React57.useRef([]);
|
|
10844
|
+
const queueRef = React57.useRef([]);
|
|
10845
|
+
const busyRef = React57.useRef(false);
|
|
10846
|
+
const stoppedRef = React57.useRef(false);
|
|
10847
|
+
const abortRef = React57.useRef(null);
|
|
10848
|
+
const serverCount = React57.useRef(0);
|
|
10849
|
+
const threadRef = React57.useRef(null);
|
|
10850
|
+
const mounted = React57.useRef(true);
|
|
10851
|
+
React57.useEffect(() => {
|
|
10746
10852
|
mounted.current = true;
|
|
10747
10853
|
return () => {
|
|
10748
10854
|
mounted.current = false;
|
|
@@ -10764,14 +10870,14 @@ function useChatEngine(opts) {
|
|
|
10764
10870
|
}
|
|
10765
10871
|
return false;
|
|
10766
10872
|
};
|
|
10767
|
-
const loadThread =
|
|
10873
|
+
const loadThread = React57.useCallback(async (id) => {
|
|
10768
10874
|
const data = await apiAdapter.getThread(id);
|
|
10769
10875
|
const list = [...data && data.messages || []];
|
|
10770
10876
|
serverCount.current = list.length;
|
|
10771
10877
|
commit(list);
|
|
10772
10878
|
return list;
|
|
10773
10879
|
}, [apiAdapter]);
|
|
10774
|
-
|
|
10880
|
+
React57.useEffect(() => {
|
|
10775
10881
|
if (!apiAdapter) {
|
|
10776
10882
|
setStatus("idle");
|
|
10777
10883
|
setFatal(null);
|
|
@@ -10984,7 +11090,7 @@ function useChatEngine(opts) {
|
|
|
10984
11090
|
await dispatchTurn(turn);
|
|
10985
11091
|
}
|
|
10986
11092
|
}
|
|
10987
|
-
const send =
|
|
11093
|
+
const send = React57.useCallback((text, attachments) => {
|
|
10988
11094
|
const body = (text || "").trim();
|
|
10989
11095
|
if (!body && !(attachments && attachments.length)) return;
|
|
10990
11096
|
if (status === "disconnected") return;
|
|
@@ -10994,7 +11100,7 @@ function useChatEngine(opts) {
|
|
|
10994
11100
|
stoppedRef.current = false;
|
|
10995
11101
|
drain();
|
|
10996
11102
|
}, [status]);
|
|
10997
|
-
const stop =
|
|
11103
|
+
const stop = React57.useCallback(() => {
|
|
10998
11104
|
stoppedRef.current = true;
|
|
10999
11105
|
const ac = abortRef.current;
|
|
11000
11106
|
if (ac) {
|
|
@@ -11013,11 +11119,11 @@ function useChatEngine(opts) {
|
|
|
11013
11119
|
store.del(STORAGE_PREFIX + threadRef.current);
|
|
11014
11120
|
}
|
|
11015
11121
|
}, [apiAdapter]);
|
|
11016
|
-
const removeQueued =
|
|
11122
|
+
const removeQueued = React57.useCallback((id) => {
|
|
11017
11123
|
queueRef.current = queueRef.current.filter((t) => t.id !== id);
|
|
11018
11124
|
setQueue(queueRef.current.slice());
|
|
11019
11125
|
}, []);
|
|
11020
|
-
const retry =
|
|
11126
|
+
const retry = React57.useCallback(() => {
|
|
11021
11127
|
const list = listRef.current;
|
|
11022
11128
|
let at = -1;
|
|
11023
11129
|
for (let i = list.length - 1; i >= 0; i--) if (list[i].role === "user") {
|
|
@@ -11033,19 +11139,19 @@ function useChatEngine(opts) {
|
|
|
11033
11139
|
setQueue(queueRef.current.slice());
|
|
11034
11140
|
drain();
|
|
11035
11141
|
}, []);
|
|
11036
|
-
const clear =
|
|
11142
|
+
const clear = React57.useCallback(() => {
|
|
11037
11143
|
commit([]);
|
|
11038
11144
|
serverCount.current = 0;
|
|
11039
11145
|
queueRef.current = [];
|
|
11040
11146
|
setQueue([]);
|
|
11041
11147
|
onClear && onClear();
|
|
11042
11148
|
}, [onClear]);
|
|
11043
|
-
const setFeedback =
|
|
11149
|
+
const setFeedback = React57.useCallback((id, value) => {
|
|
11044
11150
|
patch(id, (m) => ({ feedback: m.feedback === value ? null : value }));
|
|
11045
11151
|
const msg = listRef.current.find((m) => m.id === id);
|
|
11046
11152
|
onFeedback && onFeedback({ message: msg, feedback: msg ? msg.feedback : value });
|
|
11047
11153
|
}, [onFeedback]);
|
|
11048
|
-
const reload =
|
|
11154
|
+
const reload = React57.useCallback(async () => {
|
|
11049
11155
|
if (!threadRef.current) return;
|
|
11050
11156
|
setStatus("loading");
|
|
11051
11157
|
try {
|
|
@@ -11083,10 +11189,10 @@ var ChatKit = {
|
|
|
11083
11189
|
};
|
|
11084
11190
|
|
|
11085
11191
|
// src/components/chat/ChatTranscript.tsx
|
|
11086
|
-
import * as
|
|
11192
|
+
import * as React59 from "react";
|
|
11087
11193
|
|
|
11088
11194
|
// src/components/chat/ChatTurn.tsx
|
|
11089
|
-
import * as
|
|
11195
|
+
import * as React58 from "react";
|
|
11090
11196
|
import { jsx as jsx87, jsxs as jsxs81 } from "react/jsx-runtime";
|
|
11091
11197
|
function JsonView({ value }) {
|
|
11092
11198
|
let text;
|
|
@@ -11123,7 +11229,7 @@ function PacketCard({ packet, schema, render, onApply, applied }) {
|
|
|
11123
11229
|
] });
|
|
11124
11230
|
}
|
|
11125
11231
|
function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
|
|
11126
|
-
const [open, setOpen] =
|
|
11232
|
+
const [open, setOpen] = React58.useState(defaultOpen);
|
|
11127
11233
|
if (!text) return null;
|
|
11128
11234
|
return /* @__PURE__ */ jsxs81("div", { className: "fdc-think" + (open ? " is-open" : ""), children: [
|
|
11129
11235
|
/* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-think-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
|
|
@@ -11140,7 +11246,7 @@ function ThinkingBlock({ text, durationMs, streaming, defaultOpen = false }) {
|
|
|
11140
11246
|
] });
|
|
11141
11247
|
}
|
|
11142
11248
|
function Citations({ items = [], onOpen }) {
|
|
11143
|
-
const [open, setOpen] =
|
|
11249
|
+
const [open, setOpen] = React58.useState(false);
|
|
11144
11250
|
if (!items.length) return null;
|
|
11145
11251
|
return /* @__PURE__ */ jsxs81("div", { className: "fdc-cites", children: [
|
|
11146
11252
|
/* @__PURE__ */ jsxs81("button", { type: "button", className: "fdc-cites-head", onClick: () => setOpen((v) => !v), "aria-expanded": open, children: [
|
|
@@ -11166,7 +11272,7 @@ function clampText(text, max) {
|
|
|
11166
11272
|
return (at > max * 0.6 ? cut.slice(0, at) : cut).trimEnd() + "\u2026";
|
|
11167
11273
|
}
|
|
11168
11274
|
function MessageBody({ message: m, ctx }) {
|
|
11169
|
-
const [expanded, setExpanded] =
|
|
11275
|
+
const [expanded, setExpanded] = React58.useState(false);
|
|
11170
11276
|
const isUser = m.role === "user";
|
|
11171
11277
|
const raw = m.text || "";
|
|
11172
11278
|
const clamped = !expanded && !m.streaming ? clampText(raw, ctx.maxVisibleChars) : null;
|
|
@@ -11233,9 +11339,9 @@ function MessageBody({ message: m, ctx }) {
|
|
|
11233
11339
|
] });
|
|
11234
11340
|
}
|
|
11235
11341
|
function useCopyRun() {
|
|
11236
|
-
const [done, setDone] =
|
|
11237
|
-
const t =
|
|
11238
|
-
|
|
11342
|
+
const [done, setDone] = React58.useState(false);
|
|
11343
|
+
const t = React58.useRef(null);
|
|
11344
|
+
React58.useEffect(() => () => {
|
|
11239
11345
|
if (t.current) clearTimeout(t.current);
|
|
11240
11346
|
}, []);
|
|
11241
11347
|
return [done, (text) => {
|
|
@@ -11262,7 +11368,7 @@ function RunActions({ group, ctx }) {
|
|
|
11262
11368
|
] }),
|
|
11263
11369
|
isAssistant && ctx.onRetry ? /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act", onClick: ctx.onRetry, "aria-label": "Retry this turn", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-arrow-clockwise", "aria-hidden": "true" }) }) : null,
|
|
11264
11370
|
group.role === "user" && ctx.onEdit ? /* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act", onClick: () => ctx.onEdit(last), "aria-label": "Edit and resend", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-pencil-simple", "aria-hidden": "true" }) }) : null,
|
|
11265
|
-
isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(
|
|
11371
|
+
isAssistant && ctx.onFeedback ? /* @__PURE__ */ jsxs81(React58.Fragment, { children: [
|
|
11266
11372
|
/* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act" + (fb === "up" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "up"), "aria-pressed": fb === "up", "aria-label": "Good response", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-thumbs-up", "aria-hidden": "true" }) }),
|
|
11267
11373
|
/* @__PURE__ */ jsx87("button", { type: "button", className: "fdc-act" + (fb === "down" ? " is-on" : ""), onClick: () => ctx.onFeedback(last.id, "down"), "aria-pressed": fb === "down", "aria-label": "Bad response", children: /* @__PURE__ */ jsx87("i", { className: "ph ph-thumbs-down", "aria-hidden": "true" }) })
|
|
11268
11374
|
] }) : null,
|
|
@@ -11357,10 +11463,10 @@ function ChatTranscript({
|
|
|
11357
11463
|
renderEmpty,
|
|
11358
11464
|
className = ""
|
|
11359
11465
|
}) {
|
|
11360
|
-
const scroller =
|
|
11361
|
-
const stick =
|
|
11362
|
-
const [pill, setPill] =
|
|
11363
|
-
const seen =
|
|
11466
|
+
const scroller = React59.useRef(null);
|
|
11467
|
+
const stick = React59.useRef(true);
|
|
11468
|
+
const [pill, setPill] = React59.useState(0);
|
|
11469
|
+
const seen = React59.useRef(0);
|
|
11364
11470
|
const toBottom = (smooth) => {
|
|
11365
11471
|
const el = scroller.current;
|
|
11366
11472
|
if (!el) return;
|
|
@@ -11379,7 +11485,7 @@ function ChatTranscript({
|
|
|
11379
11485
|
seen.current = messages.length;
|
|
11380
11486
|
}
|
|
11381
11487
|
};
|
|
11382
|
-
|
|
11488
|
+
React59.useLayoutEffect(() => {
|
|
11383
11489
|
const el = scroller.current;
|
|
11384
11490
|
if (!el) return;
|
|
11385
11491
|
if (stick.current) {
|
|
@@ -11387,7 +11493,7 @@ function ChatTranscript({
|
|
|
11387
11493
|
seen.current = messages.length;
|
|
11388
11494
|
} else setPill(Math.max(0, messages.length - seen.current));
|
|
11389
11495
|
}, [messages]);
|
|
11390
|
-
|
|
11496
|
+
React59.useEffect(() => {
|
|
11391
11497
|
const el = scroller.current;
|
|
11392
11498
|
const inner = el && el.firstChild;
|
|
11393
11499
|
if (!el || !inner || typeof ResizeObserver === "undefined") return;
|
|
@@ -11397,7 +11503,7 @@ function ChatTranscript({
|
|
|
11397
11503
|
ro.observe(inner);
|
|
11398
11504
|
return () => ro.disconnect();
|
|
11399
11505
|
}, []);
|
|
11400
|
-
const groups =
|
|
11506
|
+
const groups = React59.useMemo(() => groupMessages(messages), [messages]);
|
|
11401
11507
|
const empty = !messages.length && status === "ready";
|
|
11402
11508
|
return /* @__PURE__ */ jsxs82("div", { className: ["fdc-scroll", className].filter(Boolean).join(" "), ref: scroller, onScroll, children: [
|
|
11403
11509
|
/* @__PURE__ */ jsxs82("div", { className: "fdc-log", role: "log", "aria-label": "Conversation", children: [
|
|
@@ -11421,7 +11527,7 @@ function ChatTranscript({
|
|
|
11421
11527
|
const prev = groups[i - 1];
|
|
11422
11528
|
const k = dayKey(g.messages[0].timestamp);
|
|
11423
11529
|
const showDay = !!k && (!prev || dayKey(prev.messages[0].timestamp) !== k);
|
|
11424
|
-
return /* @__PURE__ */ jsxs82(
|
|
11530
|
+
return /* @__PURE__ */ jsxs82(React59.Fragment, { children: [
|
|
11425
11531
|
showDay ? /* @__PURE__ */ jsx88("div", { className: "fdc-day", children: /* @__PURE__ */ jsx88("span", { children: dayLabel(k) }) }) : null,
|
|
11426
11532
|
/* @__PURE__ */ jsx88(ChatTurn, { group: g, ctx })
|
|
11427
11533
|
] }, g.key || i);
|
|
@@ -11438,7 +11544,7 @@ function ChatTranscript({
|
|
|
11438
11544
|
var TranscriptKit = { groupMessages };
|
|
11439
11545
|
|
|
11440
11546
|
// src/components/chat/ChatComposer.tsx
|
|
11441
|
-
import * as
|
|
11547
|
+
import * as React60 from "react";
|
|
11442
11548
|
import { jsx as jsx89, jsxs as jsxs83 } from "react/jsx-runtime";
|
|
11443
11549
|
function ChatComposer({
|
|
11444
11550
|
onSubmit,
|
|
@@ -11466,17 +11572,17 @@ function ChatComposer({
|
|
|
11466
11572
|
onReject,
|
|
11467
11573
|
onOpenAttachment
|
|
11468
11574
|
}) {
|
|
11469
|
-
const [text, setText] =
|
|
11470
|
-
const [trigger, setTrigger] =
|
|
11471
|
-
const [mentionItems, setMentionItems] =
|
|
11472
|
-
const [listening, setListening] =
|
|
11473
|
-
const [notice, setNotice] =
|
|
11474
|
-
const editor =
|
|
11475
|
-
const wrap =
|
|
11476
|
-
const stopVoice =
|
|
11575
|
+
const [text, setText] = React60.useState(draft || "");
|
|
11576
|
+
const [trigger, setTrigger] = React60.useState(null);
|
|
11577
|
+
const [mentionItems, setMentionItems] = React60.useState([]);
|
|
11578
|
+
const [listening, setListening] = React60.useState(false);
|
|
11579
|
+
const [notice, setNotice] = React60.useState(null);
|
|
11580
|
+
const editor = React60.useRef(null);
|
|
11581
|
+
const wrap = React60.useRef(null);
|
|
11582
|
+
const stopVoice = React60.useRef(null);
|
|
11477
11583
|
const staged = useStagedFiles(fileUploadHandler, { onError: () => {
|
|
11478
11584
|
} });
|
|
11479
|
-
|
|
11585
|
+
React60.useEffect(() => {
|
|
11480
11586
|
if (draft != null && draft !== text) setText(draft);
|
|
11481
11587
|
}, [draft]);
|
|
11482
11588
|
const change = (v) => {
|
|
@@ -11512,7 +11618,7 @@ function ChatComposer({
|
|
|
11512
11618
|
e.preventDefault();
|
|
11513
11619
|
staged.add(found.files);
|
|
11514
11620
|
};
|
|
11515
|
-
|
|
11621
|
+
React60.useEffect(() => {
|
|
11516
11622
|
if (!trigger || trigger.type !== "mention" || !mentionSources) {
|
|
11517
11623
|
setMentionItems([]);
|
|
11518
11624
|
return;
|
|
@@ -11530,7 +11636,7 @@ function ChatComposer({
|
|
|
11530
11636
|
const q = (trigger.query || "").toLowerCase();
|
|
11531
11637
|
setMentionItems(mentionSources.filter((m) => !q || (m.label + " " + (m.description || "")).toLowerCase().includes(q)));
|
|
11532
11638
|
}, [trigger, mentionSources]);
|
|
11533
|
-
const slashItems =
|
|
11639
|
+
const slashItems = React60.useMemo(() => {
|
|
11534
11640
|
if (!trigger || trigger.type !== "slash" || !slashCommands) return [];
|
|
11535
11641
|
const q = (trigger.query || "").toLowerCase();
|
|
11536
11642
|
return slashCommands.filter((c) => !q || (c.id + " " + c.label + " " + (c.description || "")).toLowerCase().includes(q));
|
|
@@ -11704,12 +11810,12 @@ function ChatComposer({
|
|
|
11704
11810
|
}
|
|
11705
11811
|
|
|
11706
11812
|
// src/components/chat/ChatSessionBar.tsx
|
|
11707
|
-
import * as
|
|
11813
|
+
import * as React61 from "react";
|
|
11708
11814
|
import { jsx as jsx90, jsxs as jsxs84 } from "react/jsx-runtime";
|
|
11709
11815
|
var compact2 = meterFormats.compact;
|
|
11710
11816
|
function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extras }) {
|
|
11711
|
-
const [open, setOpen] =
|
|
11712
|
-
const anchor =
|
|
11817
|
+
const [open, setOpen] = React61.useState(false);
|
|
11818
|
+
const anchor = React61.useRef(null);
|
|
11713
11819
|
const stats = sessionStats || null;
|
|
11714
11820
|
const cu = contextUsage || null;
|
|
11715
11821
|
const limits = usageLimits || null;
|
|
@@ -11724,7 +11830,7 @@ function ChatSessionBar({ sessionStats, contextUsage, usageLimits, onClear, extr
|
|
|
11724
11830
|
if (stats && stats.runningTasks) bits.push(stats.runningTasks + " running task" + (stats.runningTasks === 1 ? "" : "s"));
|
|
11725
11831
|
if (cu) bits.push(pct + "% context");
|
|
11726
11832
|
const expandable = !!(cu || limits);
|
|
11727
|
-
return /* @__PURE__ */ jsxs84(
|
|
11833
|
+
return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
|
|
11728
11834
|
/* @__PURE__ */ jsxs84("div", { className: "fdc-bar", children: [
|
|
11729
11835
|
/* @__PURE__ */ jsxs84(
|
|
11730
11836
|
"button",
|
|
@@ -11865,7 +11971,7 @@ function ModelControls({
|
|
|
11865
11971
|
] })
|
|
11866
11972
|
});
|
|
11867
11973
|
}
|
|
11868
|
-
return /* @__PURE__ */ jsxs84(
|
|
11974
|
+
return /* @__PURE__ */ jsxs84(React61.Fragment, { children: [
|
|
11869
11975
|
/* @__PURE__ */ jsx90(
|
|
11870
11976
|
MenuButton,
|
|
11871
11977
|
{
|
|
@@ -11982,11 +12088,11 @@ function AgentChatPanel({
|
|
|
11982
12088
|
onFeedback,
|
|
11983
12089
|
onEditMessage
|
|
11984
12090
|
}) {
|
|
11985
|
-
const [panelWidth, setPanelWidth] =
|
|
11986
|
-
const [applied, setApplied] =
|
|
11987
|
-
const [draft, setDraft] =
|
|
11988
|
-
const dragging =
|
|
11989
|
-
|
|
12091
|
+
const [panelWidth, setPanelWidth] = React62.useState(width);
|
|
12092
|
+
const [applied, setApplied] = React62.useState({});
|
|
12093
|
+
const [draft, setDraft] = React62.useState("");
|
|
12094
|
+
const dragging = React62.useRef(null);
|
|
12095
|
+
React62.useEffect(() => setPanelWidth(width), [width]);
|
|
11990
12096
|
const engine = useChatEngine({
|
|
11991
12097
|
contextType,
|
|
11992
12098
|
contextId,
|
|
@@ -12190,7 +12296,7 @@ function AgentChatPanel({
|
|
|
12190
12296
|
}
|
|
12191
12297
|
|
|
12192
12298
|
// src/components/files/FileViewer.tsx
|
|
12193
|
-
import * as
|
|
12299
|
+
import * as React63 from "react";
|
|
12194
12300
|
import { jsx as jsx92, jsxs as jsxs86 } from "react/jsx-runtime";
|
|
12195
12301
|
function previewKindOf(file) {
|
|
12196
12302
|
const mime = (file.mime || "").toLowerCase();
|
|
@@ -12247,13 +12353,13 @@ function parseDelimited(text, delimiter) {
|
|
|
12247
12353
|
return rows;
|
|
12248
12354
|
}
|
|
12249
12355
|
function usePreview(file, kind, resolveUrl) {
|
|
12250
|
-
const [state, setState] =
|
|
12251
|
-
const [attempt, setAttempt] =
|
|
12252
|
-
const resolveRef =
|
|
12253
|
-
|
|
12356
|
+
const [state, setState] = React63.useState({ status: "loading" });
|
|
12357
|
+
const [attempt, setAttempt] = React63.useState(0);
|
|
12358
|
+
const resolveRef = React63.useRef(resolveUrl);
|
|
12359
|
+
React63.useEffect(() => {
|
|
12254
12360
|
resolveRef.current = resolveUrl;
|
|
12255
12361
|
}, [resolveUrl]);
|
|
12256
|
-
|
|
12362
|
+
React63.useEffect(() => {
|
|
12257
12363
|
if (kind === "none") {
|
|
12258
12364
|
setState({ status: "ready" });
|
|
12259
12365
|
return;
|
|
@@ -12283,7 +12389,7 @@ function usePreview(file, kind, resolveUrl) {
|
|
|
12283
12389
|
if (ac) ac.abort();
|
|
12284
12390
|
};
|
|
12285
12391
|
}, [file.id, kind, attempt]);
|
|
12286
|
-
return { ...state, retry:
|
|
12392
|
+
return { ...state, retry: React63.useCallback(() => setAttempt((a) => a + 1), []) };
|
|
12287
12393
|
}
|
|
12288
12394
|
function PreviewBody({ file, kind, state }) {
|
|
12289
12395
|
if (kind === "image") return /* @__PURE__ */ jsx92("img", { className: "fd-fv-image", src: state.url, alt: file.name || "Preview" });
|
|
@@ -12328,13 +12434,13 @@ function PreviewBody({ file, kind, state }) {
|
|
|
12328
12434
|
] });
|
|
12329
12435
|
}
|
|
12330
12436
|
function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", onClose, actions, className = "" }) {
|
|
12331
|
-
const kind =
|
|
12437
|
+
const kind = React63.useMemo(() => previewKindOf(file), [file]);
|
|
12332
12438
|
const preview = usePreview(file, kind, resolveUrl);
|
|
12333
|
-
const [tab, setTab] =
|
|
12334
|
-
const [downloading, setDownloading] =
|
|
12335
|
-
const [downloadError, setDownloadError] =
|
|
12439
|
+
const [tab, setTab] = React63.useState("preview");
|
|
12440
|
+
const [downloading, setDownloading] = React63.useState(false);
|
|
12441
|
+
const [downloadError, setDownloadError] = React63.useState(null);
|
|
12336
12442
|
const hasSide = sidePanel != null;
|
|
12337
|
-
|
|
12443
|
+
React63.useEffect(() => {
|
|
12338
12444
|
setTab("preview");
|
|
12339
12445
|
}, [file.id]);
|
|
12340
12446
|
const download = () => {
|
|
@@ -12401,7 +12507,7 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
|
|
|
12401
12507
|
}
|
|
12402
12508
|
|
|
12403
12509
|
// src/components/files/FileBrowser.tsx
|
|
12404
|
-
import * as
|
|
12510
|
+
import * as React64 from "react";
|
|
12405
12511
|
import { Fragment as Fragment27, jsx as jsx93, jsxs as jsxs87 } from "react/jsx-runtime";
|
|
12406
12512
|
function childrenOf(folders, parentId) {
|
|
12407
12513
|
return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -12472,10 +12578,10 @@ function FolderPickerMenu({
|
|
|
12472
12578
|
destinations,
|
|
12473
12579
|
onPick
|
|
12474
12580
|
}) {
|
|
12475
|
-
const [open, setOpen] =
|
|
12581
|
+
const [open, setOpen] = React64.useState(false);
|
|
12476
12582
|
const zIndex = useOverlayLayer(open);
|
|
12477
|
-
const ref =
|
|
12478
|
-
|
|
12583
|
+
const ref = React64.useRef(null);
|
|
12584
|
+
React64.useEffect(() => {
|
|
12479
12585
|
if (!open) return;
|
|
12480
12586
|
const away = (e) => {
|
|
12481
12587
|
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
@@ -12527,8 +12633,8 @@ function FolderPickerMenu({
|
|
|
12527
12633
|
] });
|
|
12528
12634
|
}
|
|
12529
12635
|
function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
|
|
12530
|
-
const [over, setOver] =
|
|
12531
|
-
const dragCounter =
|
|
12636
|
+
const [over, setOver] = React64.useState(false);
|
|
12637
|
+
const dragCounter = React64.useRef(0);
|
|
12532
12638
|
const dropHandlers = isDropTarget ? {
|
|
12533
12639
|
onDragEnter: (e) => {
|
|
12534
12640
|
if (!hasInternalPayload(e)) return;
|
|
@@ -12576,8 +12682,8 @@ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropP
|
|
|
12576
12682
|
}
|
|
12577
12683
|
function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
|
|
12578
12684
|
const kids = childrenOf(folders, folder.id);
|
|
12579
|
-
const [expanded, setExpanded] =
|
|
12580
|
-
|
|
12685
|
+
const [expanded, setExpanded] = React64.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
|
|
12686
|
+
React64.useEffect(() => {
|
|
12581
12687
|
if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
|
|
12582
12688
|
}, [currentFolderId, folder.id, folders]);
|
|
12583
12689
|
const isCurrent = folder.id === currentFolderId;
|
|
@@ -12643,15 +12749,15 @@ function FileBrowser({
|
|
|
12643
12749
|
const canUpload = perm.canUpload !== false && !!onUpload;
|
|
12644
12750
|
const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
|
|
12645
12751
|
const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
|
|
12646
|
-
const [createOpen, setCreateOpen] =
|
|
12647
|
-
const [createName, setCreateName] =
|
|
12648
|
-
const [renameTarget, setRenameTarget] =
|
|
12649
|
-
const [renameValue, setRenameValue] =
|
|
12650
|
-
const [deleteTarget, setDeleteTarget] =
|
|
12651
|
-
const uploadInputRef =
|
|
12652
|
-
const crumbFolders =
|
|
12653
|
-
const subfolders =
|
|
12654
|
-
const currentFiles =
|
|
12752
|
+
const [createOpen, setCreateOpen] = React64.useState(false);
|
|
12753
|
+
const [createName, setCreateName] = React64.useState("");
|
|
12754
|
+
const [renameTarget, setRenameTarget] = React64.useState(null);
|
|
12755
|
+
const [renameValue, setRenameValue] = React64.useState("");
|
|
12756
|
+
const [deleteTarget, setDeleteTarget] = React64.useState(null);
|
|
12757
|
+
const uploadInputRef = React64.useRef(null);
|
|
12758
|
+
const crumbFolders = React64.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
|
|
12759
|
+
const subfolders = React64.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
|
|
12760
|
+
const currentFiles = React64.useMemo(
|
|
12655
12761
|
() => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
|
|
12656
12762
|
[files, currentFolderId]
|
|
12657
12763
|
);
|
|
@@ -12883,8 +12989,8 @@ function BreadcrumbCrumb({
|
|
|
12883
12989
|
isDropTarget,
|
|
12884
12990
|
onDropPayload
|
|
12885
12991
|
}) {
|
|
12886
|
-
const [over, setOver] =
|
|
12887
|
-
const dragCounter =
|
|
12992
|
+
const [over, setOver] = React64.useState(false);
|
|
12993
|
+
const dragCounter = React64.useRef(0);
|
|
12888
12994
|
const dropHandlers = isDropTarget ? {
|
|
12889
12995
|
onDragEnter: (e) => {
|
|
12890
12996
|
if (!hasInternalPayload(e)) return;
|
|
@@ -12913,9 +13019,27 @@ function BreadcrumbCrumb({
|
|
|
12913
13019
|
}
|
|
12914
13020
|
|
|
12915
13021
|
// src/kits/query.ts
|
|
12916
|
-
import * as
|
|
13022
|
+
import * as React65 from "react";
|
|
13023
|
+
var isMultiSelectShaped = (v) => !!v && typeof v === "object" && Array.isArray(v.values);
|
|
13024
|
+
var isRangeShaped = (v) => !!v && typeof v === "object" && ("min" in v || "max" in v);
|
|
13025
|
+
var isDateShaped = (v) => !!v && typeof v === "object" && "kind" in v;
|
|
13026
|
+
function isActiveFilterValue(v) {
|
|
13027
|
+
if (v == null || v === "" || v === false) return false;
|
|
13028
|
+
if (Array.isArray(v)) return v.length > 0;
|
|
13029
|
+
if (isMultiSelectShaped(v)) return v.values.length > 0;
|
|
13030
|
+
if (isRangeShaped(v)) return v.min != null || v.max != null;
|
|
13031
|
+
if (isDateShaped(v)) return v.kind === "withinDays" ? v.days != null : !!(v.from && v.to);
|
|
13032
|
+
return true;
|
|
13033
|
+
}
|
|
12917
13034
|
function eqFilter(get2) {
|
|
12918
|
-
return (row, value) =>
|
|
13035
|
+
return (row, value) => {
|
|
13036
|
+
if (Array.isArray(value)) return value.includes(get2(row));
|
|
13037
|
+
if (isMultiSelectShaped(value)) {
|
|
13038
|
+
const hit = value.values.some((o) => o.value === get2(row));
|
|
13039
|
+
return value.operator === "notIn" ? !hit : hit;
|
|
13040
|
+
}
|
|
13041
|
+
return get2(row) === value;
|
|
13042
|
+
};
|
|
12919
13043
|
}
|
|
12920
13044
|
function anyOfFilter(get2) {
|
|
12921
13045
|
return (row, value) => {
|
|
@@ -12926,10 +13050,15 @@ function anyOfFilter(get2) {
|
|
|
12926
13050
|
function rangeFilter(get2) {
|
|
12927
13051
|
return (row, value) => {
|
|
12928
13052
|
const n = get2(row);
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
13053
|
+
let lo = 0;
|
|
13054
|
+
let hi = Number.POSITIVE_INFINITY;
|
|
13055
|
+
if (Array.isArray(value)) {
|
|
13056
|
+
lo = Number(value[0] ?? 0);
|
|
13057
|
+
hi = value[1] == null ? Number.POSITIVE_INFINITY : Number(value[1]);
|
|
13058
|
+
} else if (isRangeShaped(value)) {
|
|
13059
|
+
lo = value.min ?? 0;
|
|
13060
|
+
hi = value.max ?? Number.POSITIVE_INFINITY;
|
|
13061
|
+
}
|
|
12933
13062
|
return n >= lo && (hi === Number.POSITIVE_INFINITY || n <= hi);
|
|
12934
13063
|
};
|
|
12935
13064
|
}
|
|
@@ -12945,22 +13074,22 @@ function compare(a, b, dir) {
|
|
|
12945
13074
|
var API = null;
|
|
12946
13075
|
var PREFS = null;
|
|
12947
13076
|
function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
|
|
12948
|
-
const [query, setQuery] =
|
|
13077
|
+
const [query, setQuery] = React65.useState(() => {
|
|
12949
13078
|
const store = PREFS || window.PlannerPrefs;
|
|
12950
13079
|
const saved = prefsKey && store ? store.getTable(prefsKey) : {};
|
|
12951
13080
|
return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
|
|
12952
13081
|
});
|
|
12953
|
-
const savePref =
|
|
13082
|
+
const savePref = React65.useCallback((patch2) => {
|
|
12954
13083
|
const store = PREFS || window.PlannerPrefs;
|
|
12955
13084
|
if (prefsKey && store) store.setTable(prefsKey, patch2);
|
|
12956
13085
|
}, [prefsKey]);
|
|
12957
|
-
const [res, setRes] =
|
|
12958
|
-
const [loading, setLoading] =
|
|
12959
|
-
const seq2 =
|
|
13086
|
+
const [res, setRes] = React65.useState(null);
|
|
13087
|
+
const [loading, setLoading] = React65.useState(true);
|
|
13088
|
+
const seq2 = React65.useRef(0);
|
|
12960
13089
|
const depKey = (deps || []).join("|");
|
|
12961
13090
|
const paramKey = JSON.stringify(params || {});
|
|
12962
13091
|
const queryKey = JSON.stringify(query);
|
|
12963
|
-
|
|
13092
|
+
React65.useEffect(() => {
|
|
12964
13093
|
const id = ++seq2.current;
|
|
12965
13094
|
setLoading(true);
|
|
12966
13095
|
const t = setTimeout(() => {
|
|
@@ -12996,7 +13125,7 @@ function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
|
|
|
12996
13125
|
loading,
|
|
12997
13126
|
/** True only on the very first load — later loads keep showing stale rows. */
|
|
12998
13127
|
initialLoading: loading && !res,
|
|
12999
|
-
activeFilterCount: Object.values(query.filters).filter(
|
|
13128
|
+
activeFilterCount: Object.values(query.filters).filter(isActiveFilterValue).length,
|
|
13000
13129
|
setPage: (page) => patch({ page }, false),
|
|
13001
13130
|
setPageSize: (pageSize) => {
|
|
13002
13131
|
savePref({ pageSize });
|
|
@@ -13059,7 +13188,6 @@ var QueryKit = {
|
|
|
13059
13188
|
const Q = { ...DEFAULTS, ...query || {} };
|
|
13060
13189
|
const c = cfg;
|
|
13061
13190
|
const filters = Q.filters || {};
|
|
13062
|
-
const active = (v) => !(v == null || v === "" || v === false || Array.isArray(v) && !v.length);
|
|
13063
13191
|
let out = rows;
|
|
13064
13192
|
const term = (Q.q || "").trim().toLowerCase();
|
|
13065
13193
|
if (term && c.searchKeys) {
|
|
@@ -13069,7 +13197,7 @@ var QueryKit = {
|
|
|
13069
13197
|
const searched = out;
|
|
13070
13198
|
Object.entries(filters).forEach(([id, val]) => {
|
|
13071
13199
|
const fn = c.filters && c.filters[id];
|
|
13072
|
-
if (!
|
|
13200
|
+
if (!isActiveFilterValue(val) || !fn) return;
|
|
13073
13201
|
out = out.filter((r) => fn(r, val));
|
|
13074
13202
|
});
|
|
13075
13203
|
const matched = out;
|
|
@@ -13116,9 +13244,30 @@ var QueryKit = {
|
|
|
13116
13244
|
if (Q.sort) parts.push("sort=" + Q.sort + "&dir=" + Q.dir);
|
|
13117
13245
|
if (Q.q) parts.push("q=" + encodeURIComponent(Q.q));
|
|
13118
13246
|
Object.entries(Q.filters || {}).forEach(([k, v]) => {
|
|
13119
|
-
if (
|
|
13120
|
-
if (Array.isArray(v))
|
|
13121
|
-
|
|
13247
|
+
if (!isActiveFilterValue(v)) return;
|
|
13248
|
+
if (Array.isArray(v)) {
|
|
13249
|
+
v.forEach((x) => parts.push(k + "=" + encodeURIComponent(x === Infinity ? "*" : x)));
|
|
13250
|
+
return;
|
|
13251
|
+
}
|
|
13252
|
+
if (isMultiSelectShaped(v)) {
|
|
13253
|
+
const prefix = v.operator === "notIn" ? "notIn:" : "";
|
|
13254
|
+
v.values.forEach((o) => parts.push(k + "=" + encodeURIComponent(prefix + String(o.value))));
|
|
13255
|
+
return;
|
|
13256
|
+
}
|
|
13257
|
+
if (isRangeShaped(v)) {
|
|
13258
|
+
if (v.min != null) parts.push(k + ".min=" + encodeURIComponent(String(v.min)));
|
|
13259
|
+
if (v.max != null) parts.push(k + ".max=" + encodeURIComponent(String(v.max)));
|
|
13260
|
+
return;
|
|
13261
|
+
}
|
|
13262
|
+
if (isDateShaped(v)) {
|
|
13263
|
+
if (v.kind === "withinDays") parts.push(k + "=withinDays:" + encodeURIComponent(String(v.days)));
|
|
13264
|
+
else {
|
|
13265
|
+
parts.push(k + ".from=" + encodeURIComponent(String(v.from)));
|
|
13266
|
+
parts.push(k + ".to=" + encodeURIComponent(String(v.to)));
|
|
13267
|
+
}
|
|
13268
|
+
return;
|
|
13269
|
+
}
|
|
13270
|
+
parts.push(k + "=" + encodeURIComponent(v));
|
|
13122
13271
|
});
|
|
13123
13272
|
return parts.join("&");
|
|
13124
13273
|
},
|
|
@@ -13259,12 +13408,12 @@ function createVersionStore(initialState, options) {
|
|
|
13259
13408
|
}
|
|
13260
13409
|
|
|
13261
13410
|
// src/kits/documentTitle.ts
|
|
13262
|
-
import * as
|
|
13411
|
+
import * as React66 from "react";
|
|
13263
13412
|
function formatDocumentTitle(routeTitle, appName) {
|
|
13264
13413
|
return [routeTitle, appName, "Flytedesk"].filter((part) => !!part && part.length > 0).join(" \xB7 ");
|
|
13265
13414
|
}
|
|
13266
13415
|
function useDocumentTitle(routeTitle, appName) {
|
|
13267
|
-
|
|
13416
|
+
React66.useEffect(() => {
|
|
13268
13417
|
if (typeof document === "undefined") return;
|
|
13269
13418
|
document.title = formatDocumentTitle(routeTitle, appName);
|
|
13270
13419
|
}, [routeTitle, appName]);
|