@flytedan/flytebot-design-system 0.45.0 → 0.47.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
@@ -207,6 +207,7 @@ __export(index_exports, {
207
207
  installFlytedeskFavicon: () => installFlytedeskFavicon,
208
208
  isImage: () => isImage,
209
209
  isSystemMessage: () => isSystemMessage,
210
+ isWithinLayerTree: () => isWithinLayerTree,
210
211
  jobBucket: () => jobBucket,
211
212
  languageLabel: () => languageLabel,
212
213
  loadPrefs: () => loadPrefs,
@@ -342,11 +343,21 @@ var import_react_dom = require("react-dom");
342
343
  // src/kits/layerStack.tsx
343
344
  var React2 = __toESM(require("react"), 1);
344
345
  var import_jsx_runtime3 = require("react/jsx-runtime");
345
- var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
346
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"]), [contenteditable]:not([contenteditable="false"])';
346
347
  var LayerContext = React2.createContext(null);
347
348
  var openLayers = /* @__PURE__ */ new Set();
348
349
  var lastOrder = 0;
349
350
  var pendingEscape = null;
351
+ var lastClosed = null;
352
+ function isOpener(el) {
353
+ return !!el && el !== document.body && el.isConnected;
354
+ }
355
+ function inheritedOpener() {
356
+ let l = lastClosed;
357
+ lastClosed = null;
358
+ for (; l; l = l.parent) if (isOpener(l.opener)) return l.opener;
359
+ return null;
360
+ }
350
361
  function rank(layer) {
351
362
  const path = [];
352
363
  for (let l = layer; l; l = l.parent) if (l.order > 0) path.push(l.order);
@@ -432,11 +443,13 @@ function open(layer) {
432
443
  }
433
444
  openLayers.add(layer);
434
445
  layer.order = ++lastOrder;
446
+ lastClosed = null;
435
447
  if (layer.options.trap && !contains(scopeOf(layer), document.activeElement)) focusInto(layer);
436
448
  }
437
449
  function close(layer) {
438
450
  openLayers.delete(layer);
439
451
  layer.order = 0;
452
+ lastClosed = layer;
440
453
  if (openLayers.size === 0) {
441
454
  document.removeEventListener("keydown", onKeydownCapture, true);
442
455
  document.removeEventListener("keydown", onKeydown);
@@ -454,7 +467,7 @@ function returnFocus(layer) {
454
467
  focusInto(l);
455
468
  return;
456
469
  }
457
- if (l.opener && l.opener.isConnected) {
470
+ if (isOpener(l.opener)) {
458
471
  l.opener.focus({ preventScroll: true });
459
472
  return;
460
473
  }
@@ -465,7 +478,9 @@ function useStackedLayer(options) {
465
478
  const [layer] = React2.useState(() => ({ parent, options, order: 0, opener: null }));
466
479
  const { active } = options;
467
480
  React2.useInsertionEffect(() => {
468
- if (active) layer.opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
481
+ if (!active) return;
482
+ const current2 = document.activeElement instanceof HTMLElement ? document.activeElement : null;
483
+ layer.opener = isOpener(current2) ? current2 : inheritedOpener();
469
484
  }, [active, layer]);
470
485
  React2.useLayoutEffect(() => {
471
486
  layer.options = options;
@@ -484,6 +499,9 @@ function useStackedLayer(options) {
484
499
  function StackedLayerProvider({ layer, children }) {
485
500
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LayerContext.Provider, { value: layer, children });
486
501
  }
502
+ function isWithinLayerTree(layer, node) {
503
+ return contains(scopeOf(layer), node);
504
+ }
487
505
 
488
506
  // src/components/actions/Popover.tsx
489
507
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -680,15 +698,14 @@ function Popover({
680
698
  const away = (e) => {
681
699
  if (!closeOnOutside) return;
682
700
  const target = e.target;
683
- if (ref.current && ref.current.contains(target)) return;
701
+ if (isWithinLayerTree(layer, target)) return;
684
702
  const trigger = (triggerRef || anchorRef).current;
685
703
  if (trigger && trigger.contains(target)) return;
686
- if (e.target.closest && e.target.closest(".fd-pop")) return;
687
704
  onClose && onClose();
688
705
  };
689
706
  document.addEventListener("pointerdown", away, true);
690
707
  return () => document.removeEventListener("pointerdown", away, true);
691
- }, [open2, closeOnOutside, onClose, anchorRef, triggerRef]);
708
+ }, [open2, closeOnOutside, onClose, anchorRef, triggerRef, layer]);
692
709
  if (!open2 || !pos) return null;
693
710
  return portal(
694
711
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StackedLayerProvider, { layer, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
@@ -3292,6 +3309,21 @@ var fmt = (s) => {
3292
3309
  const d = parse(s);
3293
3310
  return d ? MONTHS[d.getMonth()].slice(0, 3) + " " + d.getDate() + ", " + d.getFullYear() : "";
3294
3311
  };
3312
+ var fullDateLabel = (d) => new Intl.DateTimeFormat(void 0, { dateStyle: "full" }).format(d);
3313
+ var monthYearLabel = (d) => new Intl.DateTimeFormat(void 0, { month: "long", year: "numeric" }).format(d);
3314
+ var weekdayLong = (i) => new Intl.DateTimeFormat(void 0, { weekday: "long" }).format(new Date(2023, 0, 1 + i));
3315
+ var daysInMonth = (y, m) => new Date(y, m + 1, 0).getDate();
3316
+ var addMonths = (d, n) => {
3317
+ const total = d.getFullYear() * 12 + d.getMonth() + n;
3318
+ const y = Math.floor(total / 12), m = (total % 12 + 12) % 12;
3319
+ return new Date(y, m, Math.min(d.getDate(), daysInMonth(y, m)));
3320
+ };
3321
+ var addYears = (d, n) => {
3322
+ const y = d.getFullYear() + n;
3323
+ return new Date(y, d.getMonth(), Math.min(d.getDate(), daysInMonth(y, d.getMonth())));
3324
+ };
3325
+ var addDays = (d, n) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
3326
+ var sameMonth = (d, y, m) => d.getFullYear() === y && d.getMonth() === m;
3295
3327
  function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3296
3328
  const today = /* @__PURE__ */ new Date();
3297
3329
  const sel = range ? value || {} : { start: value, end: value };
@@ -3306,8 +3338,38 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3306
3338
  const lo = s, hi = e || hoverEnd;
3307
3339
  return lo && hi && d > (lo < hi ? lo : hi) && d < (lo < hi ? hi : lo);
3308
3340
  };
3309
- const isEdge = (d) => s && iso(d) === iso(s) || e && iso(d) === iso(e);
3341
+ const isStart = (d) => !!s && iso(d) === iso(s);
3342
+ const isEnd = (d) => !!e && iso(d) === iso(e);
3343
+ const isEdge = (d) => isStart(d) || isEnd(d);
3344
+ const nextY = vm === 11 ? vy + 1 : vy, nextM = (vm + 1) % 12;
3345
+ const [activeDate, setActiveDate] = React23.useState(() => {
3346
+ if (s) return s;
3347
+ if (sameMonth(today, anchor.getFullYear(), anchor.getMonth())) return today;
3348
+ return new Date(anchor.getFullYear(), anchor.getMonth(), 1);
3349
+ });
3350
+ const calRef = React23.useRef(null);
3351
+ const pageToShow = (y, m) => {
3352
+ const idx = y * 12 + m, firstIdx = vy * 12 + vm, secondIdx = firstIdx + 1;
3353
+ if (idx === firstIdx || months > 1 && idx === secondIdx) return;
3354
+ if (idx < firstIdx || months === 1) {
3355
+ setVy(y);
3356
+ setVm(m);
3357
+ return;
3358
+ }
3359
+ const baseIdx = idx - 1;
3360
+ setVy(Math.floor(baseIdx / 12));
3361
+ setVm((baseIdx % 12 + 12) % 12);
3362
+ };
3363
+ const moveActive = (next) => {
3364
+ setActiveDate(next);
3365
+ pageToShow(next.getFullYear(), next.getMonth());
3366
+ };
3367
+ React23.useEffect(() => {
3368
+ const el = calRef.current?.querySelector('[data-iso="' + iso(activeDate) + '"]');
3369
+ if (el) el.focus({ preventScroll: true });
3370
+ }, [activeDate, vy, vm]);
3310
3371
  const clickDay = (d) => {
3372
+ setActiveDate(d);
3311
3373
  const v = iso(d);
3312
3374
  if (!range) {
3313
3375
  onPick(v);
@@ -3332,36 +3394,115 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3332
3394
  setVm(m);
3333
3395
  setVy(y);
3334
3396
  };
3397
+ const onGridKeyDown = (ev) => {
3398
+ const d = activeDate;
3399
+ switch (ev.key) {
3400
+ case "ArrowLeft":
3401
+ ev.preventDefault();
3402
+ moveActive(addDays(d, -1));
3403
+ return;
3404
+ case "ArrowRight":
3405
+ ev.preventDefault();
3406
+ moveActive(addDays(d, 1));
3407
+ return;
3408
+ case "ArrowUp":
3409
+ ev.preventDefault();
3410
+ moveActive(addDays(d, -7));
3411
+ return;
3412
+ case "ArrowDown":
3413
+ ev.preventDefault();
3414
+ moveActive(addDays(d, 7));
3415
+ return;
3416
+ case "Home":
3417
+ ev.preventDefault();
3418
+ moveActive(addDays(d, -d.getDay()));
3419
+ return;
3420
+ case "End":
3421
+ ev.preventDefault();
3422
+ moveActive(addDays(d, 6 - d.getDay()));
3423
+ return;
3424
+ case "PageUp":
3425
+ ev.preventDefault();
3426
+ moveActive(ev.shiftKey ? addYears(d, -1) : addMonths(d, -1));
3427
+ return;
3428
+ case "PageDown":
3429
+ ev.preventDefault();
3430
+ moveActive(ev.shiftKey ? addYears(d, 1) : addMonths(d, 1));
3431
+ return;
3432
+ case "Enter":
3433
+ case " ":
3434
+ ev.preventDefault();
3435
+ clickDay(d);
3436
+ return;
3437
+ default:
3438
+ return;
3439
+ }
3440
+ };
3441
+ const monthHeading = mode2 === "days" ? MONTHS[vm] + " " + vy : mode2 === "months" ? String(vy) : vy - 5 + " \u2013 " + (vy + 6);
3442
+ const prevLabel = mode2 === "days" ? "Previous month, " + monthYearLabel(new Date(vy, vm - 1, 1)) : mode2 === "months" ? "Previous year, " + (vy - 1) : "Previous years, " + (vy - 17) + " \u2013 " + (vy - 6);
3443
+ const nextLabel = mode2 === "days" ? "Next month, " + monthYearLabel(new Date(vy, vm + 1, 1)) : mode2 === "months" ? "Next year, " + (vy + 1) : "Next years, " + (vy + 7) + " \u2013 " + (vy + 18);
3335
3444
  const monthGrid = (y, m) => {
3336
3445
  const startPad = new Date(y, m, 1).getDay();
3446
+ const last = daysInMonth(y, m);
3337
3447
  const cells = [];
3338
- for (let i = 0; i < 42; i++) cells.push(new Date(y, m, i - startPad + 1));
3339
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "fd-cal-grid", style: { width: months > 1 ? 252 : "auto", flex: "none" }, onMouseLeave: () => setHover(null), children: [
3340
- DOW.map((d) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "fd-cal-dow", children: d }, d)),
3341
- cells.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3342
- "button",
3343
- {
3344
- type: "button",
3345
- className: ["fd-cal-day", d.getMonth() !== m ? "is-out" : "", iso(d) === iso(today) ? "is-today" : "", inRange(d) ? "in-range" : "", isEdge(d) ? "is-edge" : ""].filter(Boolean).join(" "),
3346
- onClick: () => clickDay(d),
3347
- onMouseEnter: () => range && setHover(iso(d)),
3348
- children: d.getDate()
3349
- },
3350
- i
3351
- ))
3352
- ] });
3448
+ for (let i = 0; i < 42; i++) {
3449
+ const dom = i - startPad + 1;
3450
+ cells.push(dom >= 1 && dom <= last ? new Date(y, m, dom) : null);
3451
+ }
3452
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
3453
+ "div",
3454
+ {
3455
+ className: "fd-cal-grid",
3456
+ role: "grid",
3457
+ "aria-label": monthYearLabel(new Date(y, m, 1)),
3458
+ style: { width: months > 1 ? 252 : "auto", flex: "none" },
3459
+ onMouseLeave: () => setHover(null),
3460
+ children: [
3461
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "fd-cal-row", role: "row", children: DOW.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "fd-cal-dow", role: "columnheader", "aria-label": weekdayLong(i), children: d }, d)) }),
3462
+ Array.from({ length: 6 }, (_, row) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "fd-cal-row", role: "row", children: cells.slice(row * 7, row * 7 + 7).map((d, col) => {
3463
+ if (!d) return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "fd-cal-day is-empty", role: "presentation", "aria-hidden": "true" }, col);
3464
+ const today_ = iso(d) === iso(today);
3465
+ const start = isStart(d), end = isEnd(d), selected = isEdge(d);
3466
+ const bits = [];
3467
+ if (range && start) bits.push("start date");
3468
+ if (range && end) bits.push("end date");
3469
+ if (selected && !range) bits.push("selected");
3470
+ if (today_) bits.push("today");
3471
+ const name = bits.length ? fullDateLabel(d) + ", " + bits.join(", ") : fullDateLabel(d);
3472
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3473
+ "div",
3474
+ {
3475
+ "data-iso": iso(d),
3476
+ role: "gridcell",
3477
+ tabIndex: iso(d) === iso(activeDate) ? 0 : -1,
3478
+ "aria-label": name,
3479
+ "aria-selected": selected,
3480
+ "aria-current": today_ ? "date" : void 0,
3481
+ className: ["fd-cal-day", today_ ? "is-today" : "", inRange(d) ? "in-range" : "", selected ? "is-edge" : ""].filter(Boolean).join(" "),
3482
+ onClick: () => clickDay(d),
3483
+ onMouseEnter: () => range && setHover(iso(d)),
3484
+ onKeyDown: onGridKeyDown,
3485
+ onFocus: () => setActiveDate(d),
3486
+ children: d.getDate()
3487
+ },
3488
+ col
3489
+ );
3490
+ }) }, row))
3491
+ ]
3492
+ }
3493
+ );
3353
3494
  };
3354
- const nextY = vm === 11 ? vy + 1 : vy, nextM = (vm + 1) % 12;
3355
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "fd-cal", style: { width: months > 1 && mode2 === "days" ? "auto" : void 0 }, children: [
3495
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "fd-cal", style: { width: months > 1 && mode2 === "days" ? "auto" : void 0 }, ref: calRef, children: [
3356
3496
  /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "fd-cal-head", children: [
3357
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "fd-btn-icon", "aria-label": "Previous", onClick: () => mode2 === "years" ? setVy(vy - 12) : mode2 === "months" ? setVy(vy - 1) : nav(-1), children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("i", { className: "ph ph-caret-left" }) }),
3497
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "fd-btn-icon", "aria-label": prevLabel, onClick: () => mode2 === "years" ? setVy(vy - 12) : mode2 === "months" ? setVy(vy - 1) : nav(-1), children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("i", { className: "ph ph-caret-left" }) }),
3358
3498
  /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("button", { type: "button", className: "fd-cal-title", onClick: () => setMode(mode2 === "days" ? "months" : mode2 === "months" ? "years" : "days"), children: [
3359
- mode2 === "days" ? MONTHS[vm] + " " + vy : mode2 === "months" ? vy : vy - 5 + " \u2013 " + (vy + 6),
3499
+ monthHeading,
3360
3500
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("i", { className: "ph ph-caret-down", style: { fontSize: 10, marginLeft: 6, color: "var(--text-muted)" } })
3361
3501
  ] }),
3362
3502
  months > 1 && mode2 === "days" ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "fd-cal-title", style: { cursor: "default", background: "none" }, children: MONTHS[nextM] + " " + nextY }) : null,
3363
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "fd-btn-icon", "aria-label": "Next", onClick: () => mode2 === "years" ? setVy(vy + 12) : mode2 === "months" ? setVy(vy + 1) : nav(1), children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("i", { className: "ph ph-caret-right" }) })
3503
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "fd-btn-icon", "aria-label": nextLabel, onClick: () => mode2 === "years" ? setVy(vy + 12) : mode2 === "months" ? setVy(vy + 1) : nav(1), children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("i", { className: "ph ph-caret-right" }) })
3364
3504
  ] }),
3505
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { "aria-live": "polite", className: "fd-sr-only", children: monthHeading }),
3365
3506
  mode2 === "days" ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { style: { display: "flex", gap: 18 }, children: [
3366
3507
  monthGrid(vy, vm),
3367
3508
  months > 1 ? monthGrid(nextY, nextM) : null
@@ -3401,6 +3542,7 @@ function Calendar({ value, range = false, onPick, initialMonth, months = 1 }) {
3401
3542
  setVy(today.getFullYear());
3402
3543
  setVm(today.getMonth());
3403
3544
  setMode("days");
3545
+ moveActive(today);
3404
3546
  },
3405
3547
  children: "Today"
3406
3548
  }
@@ -14250,6 +14392,7 @@ function useDocumentTitle(routeTitle, appName) {
14250
14392
  installFlytedeskFavicon,
14251
14393
  isImage,
14252
14394
  isSystemMessage,
14395
+ isWithinLayerTree,
14253
14396
  jobBucket,
14254
14397
  languageLabel,
14255
14398
  loadPrefs,