@flytedan/flytebot-design-system 0.35.0 → 0.37.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 CHANGED
@@ -3728,16 +3728,12 @@ function MultiSelectPanel({
3728
3728
  const [unmatchedTokens, setUnmatchedTokens] = React25.useState([]);
3729
3729
  const [pasteError, setPasteError] = React25.useState(null);
3730
3730
  const pasteWrapRef = React25.useRef(null);
3731
- const [resolvedAt, setResolvedAt] = React25.useState(0);
3732
3731
  React25.useEffect(() => {
3733
3732
  if (mode2 === "paste") pasteWrapRef.current?.querySelector("textarea")?.focus();
3734
3733
  }, [mode2]);
3735
- React25.useLayoutEffect(() => {
3736
- if (resolvedAt === 0) return;
3737
- pasteWrapRef.current?.querySelector("textarea")?.focus();
3738
- }, [resolvedAt]);
3739
3734
  const doResolve = () => {
3740
3735
  if (!field.resolve) return;
3736
+ if (pasteLoading) return;
3741
3737
  const tokens = Array.from(new Set(pasteText.split(/[\n,]/).map((t) => t.trim()).filter(Boolean)));
3742
3738
  if (!tokens.length) return;
3743
3739
  setPasteLoading(true);
@@ -3758,7 +3754,6 @@ function MultiSelectPanel({
3758
3754
  return;
3759
3755
  }
3760
3756
  setPasteText("");
3761
- setResolvedAt((n) => n + 1);
3762
3757
  }, (err) => {
3763
3758
  if (!mountedRef.current) return;
3764
3759
  setPasteLoading(false);
@@ -3821,7 +3816,8 @@ function MultiSelectPanel({
3821
3816
  rows: 4,
3822
3817
  placeholder: field.pastePlaceholder || field.placeholder || "Paste names, one per line or comma-separated",
3823
3818
  value: pasteText,
3824
- disabled: pasteLoading,
3819
+ readOnly: pasteLoading,
3820
+ "aria-busy": pasteLoading || void 0,
3825
3821
  onChange: (e) => setPasteText(e.target.value),
3826
3822
  onKeyDown: (e) => {
3827
3823
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
@@ -3877,7 +3873,28 @@ function MultiSelectPanel({
3877
3873
  );
3878
3874
  }) }) : null
3879
3875
  ] });
3880
- 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;
3876
+ const resolveDisabled = pasteLoading || !pasteText.trim();
3877
+ const footer = mode2 === "paste" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3878
+ Button,
3879
+ {
3880
+ size: "sm",
3881
+ variant: "secondary",
3882
+ "aria-disabled": resolveDisabled,
3883
+ onClick: (e) => {
3884
+ if (resolveDisabled) {
3885
+ e.preventDefault();
3886
+ return;
3887
+ }
3888
+ doResolve();
3889
+ },
3890
+ onKeyDown: (e) => {
3891
+ if (resolveDisabled && (e.key === "Enter" || e.key === " ")) {
3892
+ e.preventDefault();
3893
+ }
3894
+ },
3895
+ children: pasteLoading ? "Resolving\u2026" : "Resolve"
3896
+ }
3897
+ ) : void 0;
3881
3898
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3882
3899
  Popover,
3883
3900
  {
@@ -3946,17 +3963,25 @@ function useReseedFromProps(value, applies, reseed) {
3946
3963
  if (!sameValue(value, applies())) reseed(value);
3947
3964
  }, [value, applies, reseed]);
3948
3965
  }
3949
- function usePreserveFocusAcrossRemount(seed) {
3950
- const pendingId = React25.useRef(null);
3951
- const capture = (ids) => {
3966
+ function usePreserveFocusAcrossRemount(seed, fields) {
3967
+ const pendingName = React25.useRef(null);
3968
+ const fieldsRef = useLatest(fields);
3969
+ const capture = () => {
3952
3970
  const active = document.activeElement;
3953
- pendingId.current = active instanceof HTMLElement && ids.includes(active.id) ? active.id : null;
3971
+ pendingName.current = null;
3972
+ if (!(active instanceof HTMLElement)) return;
3973
+ for (const [name, ref] of Object.entries(fieldsRef.current)) {
3974
+ if (ref.current?.contains(active)) {
3975
+ pendingName.current = name;
3976
+ break;
3977
+ }
3978
+ }
3954
3979
  };
3955
3980
  React25.useLayoutEffect(() => {
3956
- const id = pendingId.current;
3957
- if (!id) return;
3958
- pendingId.current = null;
3959
- document.getElementById(id)?.focus();
3981
+ const name = pendingName.current;
3982
+ if (!name) return;
3983
+ pendingName.current = null;
3984
+ fieldsRef.current[name]?.current?.querySelector("input,textarea,select,button")?.focus();
3960
3985
  }, [seed]);
3961
3986
  return capture;
3962
3987
  }
@@ -3970,9 +3995,11 @@ function RangePanel({
3970
3995
  }) {
3971
3996
  const [draft, setDraft] = React25.useState(() => rangeDraft(value));
3972
3997
  const [seed, setSeed] = React25.useState(0);
3973
- const preserveFocus = usePreserveFocusAcrossRemount(seed);
3974
- const minId = field.key + "-min";
3975
- const maxId = field.key + "-max";
3998
+ const minId = React25.useId();
3999
+ const maxId = React25.useId();
4000
+ const minWrapRef = React25.useRef(null);
4001
+ const maxWrapRef = React25.useRef(null);
4002
+ const preserveFocus = usePreserveFocusAcrossRemount(seed, { min: minWrapRef, max: maxWrapRef });
3976
4003
  const draftRef = React25.useRef(draft);
3977
4004
  const shown = () => {
3978
4005
  const { min, max } = draftRef.current;
@@ -3983,7 +4010,7 @@ function RangePanel({
3983
4010
  };
3984
4011
  const validity = useTextValidity(publish);
3985
4012
  useReseedFromProps(value, shown, (next) => {
3986
- preserveFocus([minId, maxId]);
4013
+ preserveFocus();
3987
4014
  draftRef.current = rangeDraft(next);
3988
4015
  setDraft(draftRef.current);
3989
4016
  validity.reset();
@@ -4001,6 +4028,16 @@ function RangePanel({
4001
4028
  setDraft(next);
4002
4029
  publish();
4003
4030
  };
4031
+ const doneClick = (e) => {
4032
+ if (doneDisabled) {
4033
+ e.preventDefault();
4034
+ return;
4035
+ }
4036
+ onClose();
4037
+ };
4038
+ const doneKeyDown = (e) => {
4039
+ if (doneDisabled && (e.key === "Enter" || e.key === " ")) e.preventDefault();
4040
+ };
4004
4041
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4005
4042
  Popover,
4006
4043
  {
@@ -4011,9 +4048,9 @@ function RangePanel({
4011
4048
  offset: 6,
4012
4049
  width: PANEL_WIDTH.range,
4013
4050
  label: field.label,
4014
- footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: onClose, disabled: doneDisabled, children: "Done" }),
4051
+ footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", "aria-disabled": doneDisabled, onClick: doneClick, onKeyDown: doneKeyDown, children: "Done" }),
4015
4052
  children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
4016
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4053
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: minWrapRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4017
4054
  NumberInput,
4018
4055
  {
4019
4056
  id: minId,
@@ -4028,8 +4065,8 @@ function RangePanel({
4028
4065
  onValidityChange: validity.report("min")
4029
4066
  },
4030
4067
  "min-" + seed
4031
- ),
4032
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4068
+ ) }),
4069
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: maxWrapRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4033
4070
  NumberInput,
4034
4071
  {
4035
4072
  id: maxId,
@@ -4045,7 +4082,7 @@ function RangePanel({
4045
4082
  onValidityChange: validity.report("max")
4046
4083
  },
4047
4084
  "max-" + seed
4048
- )
4085
+ ) })
4049
4086
  ] })
4050
4087
  }
4051
4088
  );
@@ -4072,8 +4109,9 @@ function DatePanel({
4072
4109
  const daysConfirmed = React25.useRef(value?.kind === "withinDays");
4073
4110
  const [between, setBetween] = React25.useState(initial.between);
4074
4111
  const [seed, setSeed] = React25.useState(0);
4075
- const preserveFocus = usePreserveFocusAcrossRemount(seed);
4076
- const daysId = field.key + "-days";
4112
+ const daysId = React25.useId();
4113
+ const daysWrapRef = React25.useRef(null);
4114
+ const preserveFocus = usePreserveFocusAcrossRemount(seed, { days: daysWrapRef });
4077
4115
  const shownWithinDays = () => {
4078
4116
  const days = daysRef.current;
4079
4117
  return applicable(field, days == null ? void 0 : { kind: "withinDays", days }, validity.allValid());
@@ -4089,7 +4127,7 @@ function DatePanel({
4089
4127
  return daysConfirmed.current ? shownWithinDays() : void 0;
4090
4128
  };
4091
4129
  useReseedFromProps(value, applies, (next) => {
4092
- preserveFocus([daysId]);
4130
+ preserveFocus();
4093
4131
  const d = dateDraft(field, next);
4094
4132
  setKind(d.kind);
4095
4133
  daysRef.current = d.days;
@@ -4132,6 +4170,9 @@ function DatePanel({
4132
4170
  }
4133
4171
  onClose();
4134
4172
  };
4173
+ const doneKeyDown = (e) => {
4174
+ if (doneDisabled && (e.key === "Enter" || e.key === " ")) e.preventDefault();
4175
+ };
4135
4176
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4136
4177
  Popover,
4137
4178
  {
@@ -4142,7 +4183,7 @@ function DatePanel({
4142
4183
  offset: 6,
4143
4184
  width: PANEL_WIDTH.date,
4144
4185
  label: field.label,
4145
- footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", onClick: confirm, disabled: doneDisabled, children: "Done" }),
4186
+ footer: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Button, { size: "sm", variant: "secondary", "aria-disabled": doneDisabled, onClick: confirm, onKeyDown: doneKeyDown, children: "Done" }),
4146
4187
  children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "fd-stack fd-gap-sm", children: [
4147
4188
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4148
4189
  SegmentedControl,
@@ -4156,7 +4197,7 @@ function DatePanel({
4156
4197
  onChange: (k) => switchKind(k)
4157
4198
  }
4158
4199
  ),
4159
- kind === "withinDays" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4200
+ kind === "withinDays" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: daysWrapRef, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4160
4201
  NumberInput,
4161
4202
  {
4162
4203
  id: daysId,
@@ -4170,7 +4211,7 @@ function DatePanel({
4170
4211
  onValidityChange: validity.report("days")
4171
4212
  },
4172
4213
  "days-" + seed
4173
- ) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4214
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4174
4215
  DatePicker,
4175
4216
  {
4176
4217
  range: true,