@flytedan/flytebot-design-system 0.29.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 +104 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -36
- package/dist/index.d.ts +40 -36
- package/dist/index.js +104 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/components.css +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1756,7 +1756,7 @@ var BANNER_TONE_STYLE = {
|
|
|
1756
1756
|
};
|
|
1757
1757
|
function Banner({ tone = "info", title, detail, actions, role }) {
|
|
1758
1758
|
const t = BANNER_TONE_STYLE[tone];
|
|
1759
|
-
const liveRole = role || (tone === "danger"
|
|
1759
|
+
const liveRole = role || (tone === "danger" ? "alert" : "status");
|
|
1760
1760
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1761
1761
|
"div",
|
|
1762
1762
|
{
|
|
@@ -3025,17 +3025,29 @@ function NumberInput({
|
|
|
3025
3025
|
const n = Number(s);
|
|
3026
3026
|
return isNaN(n) ? null : n;
|
|
3027
3027
|
};
|
|
3028
|
+
const requiresInteger = Number.isInteger(step);
|
|
3029
|
+
const validationMessage = (n) => {
|
|
3030
|
+
if (requiresInteger && !Number.isInteger(n)) return "Enter a whole number";
|
|
3031
|
+
if (n < min) return "Must be \u2265 " + min;
|
|
3032
|
+
if (n > max) return "Must be \u2264 " + max;
|
|
3033
|
+
return null;
|
|
3034
|
+
};
|
|
3028
3035
|
const [text, setText] = React21.useState(value == null || value === "" ? "" : String(value));
|
|
3029
3036
|
const [editing, setEditing] = React21.useState(false);
|
|
3037
|
+
const [invalidMessage, setInvalidMessage] = React21.useState(null);
|
|
3030
3038
|
const timer = React21.useRef(null);
|
|
3031
3039
|
React21.useEffect(() => {
|
|
3032
|
-
if (!editing)
|
|
3040
|
+
if (!editing) {
|
|
3041
|
+
setText(value == null || value === "" ? "" : String(value));
|
|
3042
|
+
setInvalidMessage(null);
|
|
3043
|
+
}
|
|
3033
3044
|
}, [value, editing]);
|
|
3034
3045
|
const clamp = (n) => Math.min(max, Math.max(min, n));
|
|
3035
3046
|
const fire = (n) => onChange && onChange({ target: { value: n } });
|
|
3036
3047
|
const bump = (dir, mult = 1) => {
|
|
3037
3048
|
const cur = num(text) ?? num(value) ?? (min === -Infinity ? 0 : min);
|
|
3038
3049
|
const next = clamp(cur + dir * (bigStep && mult > 1 ? bigStep : step) * (bigStep ? 1 : mult));
|
|
3050
|
+
setInvalidMessage(null);
|
|
3039
3051
|
setText(String(next));
|
|
3040
3052
|
fire(next);
|
|
3041
3053
|
};
|
|
@@ -3059,7 +3071,8 @@ function NumberInput({
|
|
|
3059
3071
|
const n = num(text);
|
|
3060
3072
|
return n == null ? "" : format ? n.toLocaleString() : String(n);
|
|
3061
3073
|
})();
|
|
3062
|
-
const
|
|
3074
|
+
const shownError = error || invalidMessage || void 0;
|
|
3075
|
+
const box = ["fd-input", "fd-input-num", shownError ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
|
|
3063
3076
|
const fieldName = label || ariaLabel;
|
|
3064
3077
|
const stepperName = (verb) => fieldName ? verb + " " + fieldName : verb;
|
|
3065
3078
|
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
|
|
@@ -3075,30 +3088,29 @@ function NumberInput({
|
|
|
3075
3088
|
disabled,
|
|
3076
3089
|
placeholder,
|
|
3077
3090
|
value: shown,
|
|
3078
|
-
"aria-invalid":
|
|
3079
|
-
"aria-describedby":
|
|
3091
|
+
"aria-invalid": shownError ? "true" : void 0,
|
|
3092
|
+
"aria-describedby": shownError ? errorId : void 0,
|
|
3080
3093
|
onFocus: () => setEditing(true),
|
|
3081
3094
|
onBlur: () => {
|
|
3082
3095
|
setEditing(false);
|
|
3083
|
-
if (text.trim() === "")
|
|
3084
|
-
fire(void 0);
|
|
3085
|
-
return;
|
|
3086
|
-
}
|
|
3087
|
-
const n = num(text);
|
|
3088
|
-
if (n != null) {
|
|
3089
|
-
const c = clamp(n);
|
|
3090
|
-
setText(String(c));
|
|
3091
|
-
fire(c);
|
|
3092
|
-
}
|
|
3096
|
+
if (text.trim() === "") fire(void 0);
|
|
3093
3097
|
},
|
|
3094
3098
|
onChange: (e) => {
|
|
3095
|
-
|
|
3096
|
-
|
|
3099
|
+
const raw = e.target.value;
|
|
3100
|
+
setText(raw);
|
|
3101
|
+
if (raw.trim() === "") {
|
|
3102
|
+
setInvalidMessage(null);
|
|
3097
3103
|
fire(void 0);
|
|
3098
3104
|
return;
|
|
3099
3105
|
}
|
|
3100
|
-
const n = num(
|
|
3101
|
-
if (n
|
|
3106
|
+
const n = num(raw);
|
|
3107
|
+
if (n == null) {
|
|
3108
|
+
setInvalidMessage("Enter a number");
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
const message = validationMessage(n);
|
|
3112
|
+
setInvalidMessage(message);
|
|
3113
|
+
if (!message) fire(n);
|
|
3102
3114
|
},
|
|
3103
3115
|
onKeyDown: (e) => {
|
|
3104
3116
|
if (e.key === "ArrowUp") {
|
|
@@ -3141,9 +3153,9 @@ function NumberInput({
|
|
|
3141
3153
|
)
|
|
3142
3154
|
] })
|
|
3143
3155
|
] }),
|
|
3144
|
-
|
|
3156
|
+
shownError ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { id: errorId, className: "fd-field-error", children: [
|
|
3145
3157
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
|
|
3146
|
-
|
|
3158
|
+
shownError
|
|
3147
3159
|
] }) : help ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "fd-field-help", children: help }) : null
|
|
3148
3160
|
] });
|
|
3149
3161
|
}
|
|
@@ -3478,7 +3490,8 @@ function MultiSelectPanel({
|
|
|
3478
3490
|
onClose
|
|
3479
3491
|
}) {
|
|
3480
3492
|
const allowedOps = operatorsFor(field);
|
|
3481
|
-
const
|
|
3493
|
+
const [draftOperator, setDraftOperator] = React24.useState(value?.operator ?? allowedOps[0]);
|
|
3494
|
+
const operator = allowedOps.length === 1 ? allowedOps[0] : value?.operator ?? draftOperator;
|
|
3482
3495
|
const selected = value?.values ?? [];
|
|
3483
3496
|
const selectedRef = React24.useRef(selected);
|
|
3484
3497
|
selectedRef.current = selected;
|
|
@@ -3486,6 +3499,10 @@ function MultiSelectPanel({
|
|
|
3486
3499
|
if (nextValues.length === 0) onRemove();
|
|
3487
3500
|
else onChange({ operator: nextOperator, values: nextValues });
|
|
3488
3501
|
};
|
|
3502
|
+
const setOperator = (op) => {
|
|
3503
|
+
setDraftOperator(op);
|
|
3504
|
+
if (selected.length > 0) commit(selected, op);
|
|
3505
|
+
};
|
|
3489
3506
|
const [mode2, setMode] = React24.useState("search");
|
|
3490
3507
|
const [query, setQuery] = React24.useState("");
|
|
3491
3508
|
const [asyncState, setAsyncState] = React24.useState(
|
|
@@ -3522,18 +3539,20 @@ function MultiSelectPanel({
|
|
|
3522
3539
|
const options = field.loadOptions ? asyncState.options ?? [] : (field.options ?? []).filter((o) => o.label.toLowerCase().includes(query.toLowerCase()));
|
|
3523
3540
|
const showLoading = !!field.loadOptions && asyncState.loading;
|
|
3524
3541
|
const showError = !!field.loadOptions && asyncState.error;
|
|
3525
|
-
const
|
|
3542
|
+
const hasListbox = !showLoading && !showError && options.length > 0;
|
|
3526
3543
|
const isSelected = (o) => selected.some((s) => s.value === o.value);
|
|
3527
3544
|
const toggle = (o) => {
|
|
3528
3545
|
const next = isSelected(o) ? selected.filter((s) => s.value !== o.value) : [...selected, o];
|
|
3529
3546
|
commit(next);
|
|
3530
3547
|
};
|
|
3531
3548
|
const nav = useListboxNav({ count: options.length, onSelect: (i) => toggle(options[i]) });
|
|
3532
|
-
useListboxScrollIntoView(nav.active, listRef,
|
|
3549
|
+
useListboxScrollIntoView(nav.active, listRef, hasListbox);
|
|
3533
3550
|
React24.useEffect(() => {
|
|
3534
3551
|
nav.setActive(0);
|
|
3535
3552
|
}, [query, options.length]);
|
|
3536
|
-
const activeId =
|
|
3553
|
+
const activeId = hasListbox && options[nav.active] ? listId + "-opt-" + nav.active : void 0;
|
|
3554
|
+
const liveRole = showError ? "alert" : "status";
|
|
3555
|
+
const liveMessage = showLoading ? "Loading\u2026" : showError ? "Couldn't load options." : options.length === 0 ? "No matches" : "";
|
|
3537
3556
|
const [pasteText, setPasteText] = React24.useState("");
|
|
3538
3557
|
const [pasteLoading, setPasteLoading] = React24.useState(false);
|
|
3539
3558
|
const [unmatchedTokens, setUnmatchedTokens] = React24.useState([]);
|
|
@@ -3561,7 +3580,7 @@ function MultiSelectPanel({
|
|
|
3561
3580
|
});
|
|
3562
3581
|
};
|
|
3563
3582
|
const header = /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3564
|
-
allowedOps.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(OperatorToggle, { operator, onChange:
|
|
3583
|
+
allowedOps.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(OperatorToggle, { operator, onChange: setOperator, label: field.label }) : null,
|
|
3565
3584
|
field.resolve ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3566
3585
|
SegmentedControl,
|
|
3567
3586
|
{
|
|
@@ -3578,8 +3597,8 @@ function MultiSelectPanel({
|
|
|
3578
3597
|
{
|
|
3579
3598
|
ref: inputRef,
|
|
3580
3599
|
role: "combobox",
|
|
3581
|
-
"aria-expanded":
|
|
3582
|
-
"aria-controls":
|
|
3600
|
+
"aria-expanded": hasListbox,
|
|
3601
|
+
"aria-controls": hasListbox ? listId : void 0,
|
|
3583
3602
|
"aria-autocomplete": "list",
|
|
3584
3603
|
"aria-activedescendant": activeId,
|
|
3585
3604
|
"aria-label": field.label,
|
|
@@ -3635,33 +3654,41 @@ function MultiSelectPanel({
|
|
|
3635
3654
|
] }),
|
|
3636
3655
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-row fd-wrap fd-gap-xs", children: selected.map((v) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Tag, { onRemove: () => commit(selected.filter((s) => s.value !== v.value)), children: v.label }, v.value)) })
|
|
3637
3656
|
] }) : null
|
|
3638
|
-
] }) :
|
|
3639
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.
|
|
3640
|
-
|
|
3641
|
-
] }) : showError ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3642
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm", role: "alert", children: "Couldn't load options." }),
|
|
3643
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: () => setRetryTick((n) => n + 1), children: "Retry" })
|
|
3644
|
-
] }) : options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm fd-muted", role: "status", children: "No matches" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { id: listId, role: "listbox", "aria-label": field.label, "aria-multiselectable": "true", ref: listRef, className: "fd-pop-list", children: options.map((o, i) => {
|
|
3645
|
-
const on = isSelected(o);
|
|
3646
|
-
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
3647
|
-
"div",
|
|
3657
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3658
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
3659
|
+
"span",
|
|
3648
3660
|
{
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
role: "option",
|
|
3652
|
-
tabIndex: -1,
|
|
3653
|
-
"aria-selected": on,
|
|
3654
|
-
className: ["fd-opt", i === nav.active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
3655
|
-
onMouseEnter: () => nav.setActive(i),
|
|
3656
|
-
onClick: () => toggle(o),
|
|
3661
|
+
className: "fd-row fd-gap-xs" + (liveMessage && !showError ? " fd-muted" : ""),
|
|
3662
|
+
role: liveRole,
|
|
3657
3663
|
children: [
|
|
3658
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("
|
|
3659
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-
|
|
3664
|
+
showLoading ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-spinner", "aria-hidden": "true" }) : null,
|
|
3665
|
+
liveMessage ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-body-sm", children: liveMessage }) : null
|
|
3660
3666
|
]
|
|
3661
|
-
}
|
|
3662
|
-
|
|
3663
|
-
)
|
|
3664
|
-
|
|
3667
|
+
}
|
|
3668
|
+
),
|
|
3669
|
+
showError ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: () => setRetryTick((n) => n + 1), children: "Retry" }) : null,
|
|
3670
|
+
hasListbox ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { id: listId, role: "listbox", "aria-label": field.label, "aria-multiselectable": "true", ref: listRef, className: "fd-pop-list", children: options.map((o, i) => {
|
|
3671
|
+
const on = isSelected(o);
|
|
3672
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
3673
|
+
"div",
|
|
3674
|
+
{
|
|
3675
|
+
id: listId + "-opt-" + i,
|
|
3676
|
+
"data-i": i,
|
|
3677
|
+
role: "option",
|
|
3678
|
+
tabIndex: -1,
|
|
3679
|
+
"aria-selected": on,
|
|
3680
|
+
className: ["fd-opt", i === nav.active ? "is-active" : "", on ? "is-selected" : ""].filter(Boolean).join(" "),
|
|
3681
|
+
onMouseEnter: () => nav.setActive(i),
|
|
3682
|
+
onClick: () => toggle(o),
|
|
3683
|
+
children: [
|
|
3684
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("i", { className: "fd-filterbar-opt-check ph " + (on ? "ph-check-square is-checked" : "ph-square"), "aria-hidden": "true" }),
|
|
3685
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "fd-opt-label", children: o.label })
|
|
3686
|
+
]
|
|
3687
|
+
},
|
|
3688
|
+
o.value
|
|
3689
|
+
);
|
|
3690
|
+
}) }) : null
|
|
3691
|
+
] });
|
|
3665
3692
|
const footer = mode2 === "paste" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: doResolve, disabled: pasteLoading || !pasteText.trim(), children: pasteLoading ? "Resolving\u2026" : "Resolve" }) : void 0;
|
|
3666
3693
|
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3667
3694
|
Popover,
|
|
@@ -3710,7 +3737,7 @@ function RangePanel({
|
|
|
3710
3737
|
offset: 6,
|
|
3711
3738
|
width: PANEL_WIDTH.range,
|
|
3712
3739
|
label: field.label,
|
|
3713
|
-
footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, children: "Done" }),
|
|
3740
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, disabled: invalid, children: "Done" }),
|
|
3714
3741
|
children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3715
3742
|
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3716
3743
|
NumberInput,
|
|
@@ -3750,33 +3777,39 @@ function DatePanel({
|
|
|
3750
3777
|
onClose
|
|
3751
3778
|
}) {
|
|
3752
3779
|
const defaultDays = field.defaultDays ?? 30;
|
|
3753
|
-
const
|
|
3780
|
+
const maxDays = field.maxDays ?? 3650;
|
|
3781
|
+
const [kind, setKind] = React24.useState(value?.kind ?? "withinDays");
|
|
3782
|
+
const [daysDraft, setDaysDraft] = React24.useState(
|
|
3783
|
+
value?.kind === "withinDays" ? value.days : defaultDays
|
|
3784
|
+
);
|
|
3785
|
+
const [between, setBetween] = React24.useState(
|
|
3786
|
+
value?.kind === "between" ? { from: value.from, to: value.to } : { from: "", to: "" }
|
|
3787
|
+
);
|
|
3754
3788
|
const committedRef = React24.useRef(!!value);
|
|
3755
3789
|
const applyWithinDays = (days) => {
|
|
3756
|
-
|
|
3757
|
-
setDraft(next);
|
|
3790
|
+
setDaysDraft(days);
|
|
3758
3791
|
if (days != null) {
|
|
3759
3792
|
committedRef.current = true;
|
|
3760
|
-
onChange(
|
|
3793
|
+
onChange({ kind: "withinDays", days });
|
|
3794
|
+
} else if (committedRef.current) {
|
|
3795
|
+
committedRef.current = false;
|
|
3796
|
+
onRemove();
|
|
3761
3797
|
}
|
|
3762
3798
|
};
|
|
3763
3799
|
const applyBetween = (from, to) => {
|
|
3764
|
-
|
|
3765
|
-
setDraft(next);
|
|
3800
|
+
setBetween({ from, to });
|
|
3766
3801
|
if (from && to) {
|
|
3767
3802
|
committedRef.current = true;
|
|
3768
|
-
onChange(
|
|
3803
|
+
onChange({ kind: "between", from, to });
|
|
3769
3804
|
} else if (committedRef.current) {
|
|
3770
3805
|
committedRef.current = false;
|
|
3771
3806
|
onRemove();
|
|
3772
3807
|
}
|
|
3773
3808
|
};
|
|
3774
|
-
const switchKind = (
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
} else {
|
|
3779
|
-
setDraft({ kind: "between", from: "", to: "" });
|
|
3809
|
+
const switchKind = (next) => {
|
|
3810
|
+
setKind(next);
|
|
3811
|
+
if (next === "between") {
|
|
3812
|
+
setBetween({ from: "", to: "" });
|
|
3780
3813
|
if (committedRef.current) {
|
|
3781
3814
|
committedRef.current = false;
|
|
3782
3815
|
onRemove();
|
|
@@ -3784,9 +3817,9 @@ function DatePanel({
|
|
|
3784
3817
|
}
|
|
3785
3818
|
};
|
|
3786
3819
|
const confirm = () => {
|
|
3787
|
-
if (
|
|
3820
|
+
if (kind === "withinDays" && daysDraft != null) {
|
|
3788
3821
|
committedRef.current = true;
|
|
3789
|
-
onChange(
|
|
3822
|
+
onChange({ kind: "withinDays", days: daysDraft });
|
|
3790
3823
|
}
|
|
3791
3824
|
onClose();
|
|
3792
3825
|
};
|
|
@@ -3810,16 +3843,16 @@ function DatePanel({
|
|
|
3810
3843
|
{ value: "withinDays", label: "Within last N days" },
|
|
3811
3844
|
{ value: "between", label: "Between dates" }
|
|
3812
3845
|
],
|
|
3813
|
-
value:
|
|
3846
|
+
value: kind,
|
|
3814
3847
|
onChange: (k) => switchKind(k)
|
|
3815
3848
|
}
|
|
3816
3849
|
),
|
|
3817
|
-
|
|
3850
|
+
kind === "withinDays" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(NumberInput, { label: "Days", min: 1, max: maxDays, step: 1, value: daysDraft ?? "", onChange: (e) => applyWithinDays(e.target.value) }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3818
3851
|
DatePicker,
|
|
3819
3852
|
{
|
|
3820
3853
|
range: true,
|
|
3821
3854
|
label: "Date range",
|
|
3822
|
-
value: { start:
|
|
3855
|
+
value: { start: between.from, end: between.to },
|
|
3823
3856
|
onChange: (e) => applyBetween(e.target.value.start || "", e.target.value.end || "")
|
|
3824
3857
|
}
|
|
3825
3858
|
)
|