@flytedan/flytebot-design-system 0.37.0 → 0.39.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 +30 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -3
- package/dist/index.d.ts +20 -3
- package/dist/index.js +30 -35
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/styles/components.css +64 -17
package/dist/index.d.cts
CHANGED
|
@@ -19,8 +19,18 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
19
19
|
loading?: boolean;
|
|
20
20
|
block?: boolean;
|
|
21
21
|
as?: "button" | "a";
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/** When `disabled` (or `loading`), stay in the tab order instead of going natively
|
|
23
|
+
* `disabled`: renders `aria-disabled="true"` only while actually disabled/loading (never
|
|
24
|
+
* the `disabled` attribute), keeps the `:focus-visible` ring, and swallows
|
|
25
|
+
* click/Enter/Space — including never letting a click bubble to an ancestor's own
|
|
26
|
+
* handler — without ever submitting a form. Use whenever a button's disabled state can
|
|
27
|
+
* flip while it holds focus (e.g. a controlled panel re-seeding mid-edit) — a native
|
|
28
|
+
* `disabled` drops focus to `<body>` the instant it applies, and nothing can intercept or
|
|
29
|
+
* redirect that. Once enabled again, this behaves exactly like a plain button: no
|
|
30
|
+
* `aria-disabled` attribute at all. */
|
|
31
|
+
focusableWhenDisabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare function Button({ children, variant, size, icon, trailingIcon, arrow, loading, disabled, focusableWhenDisabled, block, as, className, onClick, onKeyDown, ...rest }: ButtonProps): React.JSX.Element;
|
|
24
34
|
|
|
25
35
|
/** A square action carrying only a Phosphor glyph. Always needs a label for a11y. */
|
|
26
36
|
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -2239,12 +2249,19 @@ interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement
|
|
|
2239
2249
|
error?: string;
|
|
2240
2250
|
required?: boolean;
|
|
2241
2251
|
rows?: number;
|
|
2252
|
+
/** Read-only plus `aria-busy`, with the same visible look as `disabled` (the
|
|
2253
|
+
* `.fd-input-disabled` tokens) — for a value an in-flight request is about to replace
|
|
2254
|
+
* while the field stays reachable. Unlike `disabled`, `busy` never sets the native
|
|
2255
|
+
* `disabled` attribute: `readOnly` blocks editing without removing the control from the
|
|
2256
|
+
* tab order or dropping focus that was already on it, so a keyboard user's in-progress
|
|
2257
|
+
* action (e.g. Ctrl+Enter to resolve) still reaches it while busy. */
|
|
2258
|
+
busy?: boolean;
|
|
2242
2259
|
/** Applied to the outer .fd-field element — this is where width belongs. */
|
|
2243
2260
|
style?: React.CSSProperties;
|
|
2244
2261
|
/** Applied to the outer .fd-field element, like `style` — the same rule as Input. */
|
|
2245
2262
|
className?: string;
|
|
2246
2263
|
}
|
|
2247
|
-
declare function Textarea({ label, help, error, required, rows, disabled, id, className, style, ...rest }: TextareaProps): React.JSX.Element;
|
|
2264
|
+
declare function Textarea({ label, help, error, required, rows, disabled, busy, readOnly, id, className, style, ...rest }: TextareaProps): React.JSX.Element;
|
|
2248
2265
|
|
|
2249
2266
|
/** Numeric field with thousands formatting while unfocused, +/- steppers (press once for
|
|
2250
2267
|
* one step, press and hold to repeat with acceleration), ArrowUp/ArrowDown (Shift for
|
package/dist/index.d.ts
CHANGED
|
@@ -19,8 +19,18 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
19
19
|
loading?: boolean;
|
|
20
20
|
block?: boolean;
|
|
21
21
|
as?: "button" | "a";
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/** When `disabled` (or `loading`), stay in the tab order instead of going natively
|
|
23
|
+
* `disabled`: renders `aria-disabled="true"` only while actually disabled/loading (never
|
|
24
|
+
* the `disabled` attribute), keeps the `:focus-visible` ring, and swallows
|
|
25
|
+
* click/Enter/Space — including never letting a click bubble to an ancestor's own
|
|
26
|
+
* handler — without ever submitting a form. Use whenever a button's disabled state can
|
|
27
|
+
* flip while it holds focus (e.g. a controlled panel re-seeding mid-edit) — a native
|
|
28
|
+
* `disabled` drops focus to `<body>` the instant it applies, and nothing can intercept or
|
|
29
|
+
* redirect that. Once enabled again, this behaves exactly like a plain button: no
|
|
30
|
+
* `aria-disabled` attribute at all. */
|
|
31
|
+
focusableWhenDisabled?: boolean;
|
|
32
|
+
}
|
|
33
|
+
declare function Button({ children, variant, size, icon, trailingIcon, arrow, loading, disabled, focusableWhenDisabled, block, as, className, onClick, onKeyDown, ...rest }: ButtonProps): React.JSX.Element;
|
|
24
34
|
|
|
25
35
|
/** A square action carrying only a Phosphor glyph. Always needs a label for a11y. */
|
|
26
36
|
interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -2239,12 +2249,19 @@ interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement
|
|
|
2239
2249
|
error?: string;
|
|
2240
2250
|
required?: boolean;
|
|
2241
2251
|
rows?: number;
|
|
2252
|
+
/** Read-only plus `aria-busy`, with the same visible look as `disabled` (the
|
|
2253
|
+
* `.fd-input-disabled` tokens) — for a value an in-flight request is about to replace
|
|
2254
|
+
* while the field stays reachable. Unlike `disabled`, `busy` never sets the native
|
|
2255
|
+
* `disabled` attribute: `readOnly` blocks editing without removing the control from the
|
|
2256
|
+
* tab order or dropping focus that was already on it, so a keyboard user's in-progress
|
|
2257
|
+
* action (e.g. Ctrl+Enter to resolve) still reaches it while busy. */
|
|
2258
|
+
busy?: boolean;
|
|
2242
2259
|
/** Applied to the outer .fd-field element — this is where width belongs. */
|
|
2243
2260
|
style?: React.CSSProperties;
|
|
2244
2261
|
/** Applied to the outer .fd-field element, like `style` — the same rule as Input. */
|
|
2245
2262
|
className?: string;
|
|
2246
2263
|
}
|
|
2247
|
-
declare function Textarea({ label, help, error, required, rows, disabled, id, className, style, ...rest }: TextareaProps): React.JSX.Element;
|
|
2264
|
+
declare function Textarea({ label, help, error, required, rows, disabled, busy, readOnly, id, className, style, ...rest }: TextareaProps): React.JSX.Element;
|
|
2248
2265
|
|
|
2249
2266
|
/** Numeric field with thousands formatting while unfocused, +/- steppers (press once for
|
|
2250
2267
|
* one step, press and hold to repeat with acceleration), ArrowUp/ArrowDown (Shift for
|
package/dist/index.js
CHANGED
|
@@ -17,12 +17,16 @@ function Button({
|
|
|
17
17
|
arrow = false,
|
|
18
18
|
loading = false,
|
|
19
19
|
disabled = false,
|
|
20
|
+
focusableWhenDisabled = false,
|
|
20
21
|
block = false,
|
|
21
22
|
as = "button",
|
|
22
23
|
className = "",
|
|
24
|
+
onClick,
|
|
25
|
+
onKeyDown,
|
|
23
26
|
...rest
|
|
24
27
|
}) {
|
|
25
28
|
const Tag2 = as;
|
|
29
|
+
const inert = (disabled || loading) && focusableWhenDisabled;
|
|
26
30
|
const cls = [
|
|
27
31
|
"fd-btn",
|
|
28
32
|
VARIANTS[variant] || VARIANTS.primary,
|
|
@@ -35,9 +39,14 @@ function Button({
|
|
|
35
39
|
Tag2,
|
|
36
40
|
{
|
|
37
41
|
className: cls,
|
|
38
|
-
disabled: Tag2 === "button" ? disabled || loading : void 0,
|
|
42
|
+
disabled: Tag2 === "button" ? (disabled || loading) && !focusableWhenDisabled : void 0,
|
|
39
43
|
"aria-disabled": disabled || loading ? "true" : void 0,
|
|
40
44
|
"aria-busy": loading ? "true" : void 0,
|
|
45
|
+
onClick: inert ? (e) => {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
e.stopPropagation();
|
|
48
|
+
} : onClick,
|
|
49
|
+
onKeyDown,
|
|
41
50
|
...rest,
|
|
42
51
|
children: [
|
|
43
52
|
icon && !loading ? /* @__PURE__ */ jsx("i", { className: "ph ph-" + icon, "aria-hidden": "true" }) : null,
|
|
@@ -3196,12 +3205,23 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
|
|
|
3196
3205
|
|
|
3197
3206
|
// src/components/forms/Textarea.tsx
|
|
3198
3207
|
import { jsx as jsx44, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3199
|
-
function Textarea({ label, help, error, required = false, rows = 4, disabled = false, id, className = "", style, ...rest }) {
|
|
3208
|
+
function Textarea({ label, help, error, required = false, rows = 4, disabled = false, busy = false, readOnly, id, className = "", style, ...rest }) {
|
|
3200
3209
|
const fieldId = useFieldId(id);
|
|
3201
|
-
const box = ["fd-input", "fd-input-textarea", error ? "fd-input-error" : "", disabled ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
|
|
3210
|
+
const box = ["fd-input", "fd-input-textarea", error ? "fd-input-error" : "", disabled || busy ? "fd-input-disabled" : ""].filter(Boolean).join(" ");
|
|
3202
3211
|
return /* @__PURE__ */ jsxs38("div", { className: ["fd-field", className].filter(Boolean).join(" "), style, children: [
|
|
3203
3212
|
label ? /* @__PURE__ */ jsx44(FieldLabel, { htmlFor: fieldId, required, children: label }) : null,
|
|
3204
|
-
/* @__PURE__ */ jsx44("div", { className: box, children: /* @__PURE__ */ jsx44(
|
|
3213
|
+
/* @__PURE__ */ jsx44("div", { className: box, children: /* @__PURE__ */ jsx44(
|
|
3214
|
+
"textarea",
|
|
3215
|
+
{
|
|
3216
|
+
id: fieldId,
|
|
3217
|
+
rows,
|
|
3218
|
+
disabled,
|
|
3219
|
+
readOnly: busy || readOnly,
|
|
3220
|
+
"aria-busy": busy || void 0,
|
|
3221
|
+
"aria-invalid": error ? "true" : void 0,
|
|
3222
|
+
...rest
|
|
3223
|
+
}
|
|
3224
|
+
) }),
|
|
3205
3225
|
error ? /* @__PURE__ */ jsxs38("span", { className: "fd-field-error", children: [
|
|
3206
3226
|
/* @__PURE__ */ jsx44("i", { className: "ph ph-warning-circle", "aria-hidden": "true" }),
|
|
3207
3227
|
error
|
|
@@ -3576,8 +3596,7 @@ function MultiSelectPanel({
|
|
|
3576
3596
|
rows: 4,
|
|
3577
3597
|
placeholder: field.pastePlaceholder || field.placeholder || "Paste names, one per line or comma-separated",
|
|
3578
3598
|
value: pasteText,
|
|
3579
|
-
|
|
3580
|
-
"aria-busy": pasteLoading || void 0,
|
|
3599
|
+
busy: pasteLoading,
|
|
3581
3600
|
onChange: (e) => setPasteText(e.target.value),
|
|
3582
3601
|
onKeyDown: (e) => {
|
|
3583
3602
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
@@ -3639,19 +3658,9 @@ function MultiSelectPanel({
|
|
|
3639
3658
|
{
|
|
3640
3659
|
size: "sm",
|
|
3641
3660
|
variant: "secondary",
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
e.preventDefault();
|
|
3646
|
-
return;
|
|
3647
|
-
}
|
|
3648
|
-
doResolve();
|
|
3649
|
-
},
|
|
3650
|
-
onKeyDown: (e) => {
|
|
3651
|
-
if (resolveDisabled && (e.key === "Enter" || e.key === " ")) {
|
|
3652
|
-
e.preventDefault();
|
|
3653
|
-
}
|
|
3654
|
-
},
|
|
3661
|
+
disabled: resolveDisabled,
|
|
3662
|
+
focusableWhenDisabled: true,
|
|
3663
|
+
onClick: doResolve,
|
|
3655
3664
|
children: pasteLoading ? "Resolving\u2026" : "Resolve"
|
|
3656
3665
|
}
|
|
3657
3666
|
) : void 0;
|
|
@@ -3788,16 +3797,6 @@ function RangePanel({
|
|
|
3788
3797
|
setDraft(next);
|
|
3789
3798
|
publish();
|
|
3790
3799
|
};
|
|
3791
|
-
const doneClick = (e) => {
|
|
3792
|
-
if (doneDisabled) {
|
|
3793
|
-
e.preventDefault();
|
|
3794
|
-
return;
|
|
3795
|
-
}
|
|
3796
|
-
onClose();
|
|
3797
|
-
};
|
|
3798
|
-
const doneKeyDown = (e) => {
|
|
3799
|
-
if (doneDisabled && (e.key === "Enter" || e.key === " ")) e.preventDefault();
|
|
3800
|
-
};
|
|
3801
3800
|
return /* @__PURE__ */ jsx45(
|
|
3802
3801
|
Popover,
|
|
3803
3802
|
{
|
|
@@ -3808,7 +3807,7 @@ function RangePanel({
|
|
|
3808
3807
|
offset: 6,
|
|
3809
3808
|
width: PANEL_WIDTH.range,
|
|
3810
3809
|
label: field.label,
|
|
3811
|
-
footer: /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary",
|
|
3810
|
+
footer: /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", disabled: doneDisabled, focusableWhenDisabled: true, onClick: onClose, children: "Done" }),
|
|
3812
3811
|
children: /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3813
3812
|
/* @__PURE__ */ jsx45("span", { ref: minWrapRef, children: /* @__PURE__ */ jsx45(
|
|
3814
3813
|
NumberInput,
|
|
@@ -3923,16 +3922,12 @@ function DatePanel({
|
|
|
3923
3922
|
};
|
|
3924
3923
|
const doneDisabled = kind === "withinDays" && !validity.allValid();
|
|
3925
3924
|
const confirm = () => {
|
|
3926
|
-
if (doneDisabled) return;
|
|
3927
3925
|
if (kind === "withinDays") {
|
|
3928
3926
|
const shown = shownWithinDays();
|
|
3929
3927
|
if (shown) onCommit(shown);
|
|
3930
3928
|
}
|
|
3931
3929
|
onClose();
|
|
3932
3930
|
};
|
|
3933
|
-
const doneKeyDown = (e) => {
|
|
3934
|
-
if (doneDisabled && (e.key === "Enter" || e.key === " ")) e.preventDefault();
|
|
3935
|
-
};
|
|
3936
3931
|
return /* @__PURE__ */ jsx45(
|
|
3937
3932
|
Popover,
|
|
3938
3933
|
{
|
|
@@ -3943,7 +3938,7 @@ function DatePanel({
|
|
|
3943
3938
|
offset: 6,
|
|
3944
3939
|
width: PANEL_WIDTH.date,
|
|
3945
3940
|
label: field.label,
|
|
3946
|
-
footer: /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary",
|
|
3941
|
+
footer: /* @__PURE__ */ jsx45(Button, { size: "sm", variant: "secondary", disabled: doneDisabled, focusableWhenDisabled: true, onClick: confirm, children: "Done" }),
|
|
3947
3942
|
children: /* @__PURE__ */ jsxs39("span", { className: "fd-stack fd-gap-sm", children: [
|
|
3948
3943
|
/* @__PURE__ */ jsx45(
|
|
3949
3944
|
SegmentedControl,
|