@planningcenter/topbar 12.4.0 → 12.4.1

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.
@@ -34427,7 +34427,8 @@ function AskAIButton({
34427
34427
  onClick: () => onClick(),
34428
34428
  type: "button"
34429
34429
  }, /*#__PURE__*/React__default.createElement(SparkleIcon, {
34430
- className: styles.sparkle
34430
+ className: styles.sparkle,
34431
+ fill: "currentColor"
34431
34432
  }), /*#__PURE__*/React__default.createElement("span", {
34432
34433
  className: styles.label
34433
34434
  }, text));
@@ -34616,6 +34617,9 @@ function createPicobotIframe(picobotOrigin, {
34616
34617
  // Contact support) and picobot's own NEW_ROUTE_RENDERED reports—so it
34617
34618
  // always reflects the live conversation. See buildPicobotSrc.
34618
34619
  const [loadedPath, setLoadedPath] = React__default.useState(iframePath || "");
34620
+ // Hidden until picobot paints so its blank first frame doesn't flash
34621
+ // white against the drawer in dark mode.
34622
+ const [isReady, setIsReady] = React__default.useState(false);
34619
34623
  const hasReportedErrorRef = React__default.useRef(false);
34620
34624
  const loadTimeoutRef = React__default.useRef();
34621
34625
  const reportLoadError = React__default.useCallback(() => {
@@ -34643,7 +34647,10 @@ function createPicobotIframe(picobotOrigin, {
34643
34647
  setLoadedPath(reportedPath);
34644
34648
  onIframeChangePath(reportedPath);
34645
34649
  }
34646
- if (((_c = e.data) === null || _c === void 0 ? void 0 : _c.type) === "READY") clearTimeout(loadTimeoutRef.current);
34650
+ if (((_c = e.data) === null || _c === void 0 ? void 0 : _c.type) === "READY") {
34651
+ clearTimeout(loadTimeoutRef.current);
34652
+ setIsReady(true);
34653
+ }
34647
34654
  if (onMessage) onMessage(e.data);
34648
34655
  }
34649
34656
  window.addEventListener("message", handleGuestMessage);
@@ -34653,12 +34660,15 @@ function createPicobotIframe(picobotOrigin, {
34653
34660
  setLoadedPath(iframePath || "");
34654
34661
  }, [iframePath]);
34655
34662
  // A one-way channel for sending messages to picobot. Topbar just
34656
- // forwards the envelope to the iframe and holds no message semantics of
34657
- // its own—the consumer decides what to send and when.
34663
+ // forwards the envelope to the iframe—the consumer decides what to send
34664
+ // and when. The one exception is PANE_VISIBLE: Safari ignores focus()
34665
+ // from inside a cross-origin iframe, so the host has to focus the iframe
34666
+ // before picobot can focus its composer.
34658
34667
  React__default.useImperativeHandle(ref, () => ({
34659
34668
  postMessage(message) {
34660
- var _a, _b;
34661
- (_b = (_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.postMessage(message, picobotOrigin);
34669
+ var _a, _b, _c;
34670
+ if (message.type === "PANE_VISIBLE") (_a = iframeRef.current) === null || _a === void 0 ? void 0 : _a.focus();
34671
+ (_c = (_b = iframeRef.current) === null || _b === void 0 ? void 0 : _b.contentWindow) === null || _c === void 0 ? void 0 : _c.postMessage(message, picobotOrigin);
34662
34672
  }
34663
34673
  }), []);
34664
34674
  const iframeSrc = buildPicobotSrc(picobotOrigin, loadedPath, {
@@ -34672,6 +34682,7 @@ function createPicobotIframe(picobotOrigin, {
34672
34682
  border: 0,
34673
34683
  flex: 1,
34674
34684
  height: "100%",
34685
+ visibility: isReady ? undefined : "hidden",
34675
34686
  width: "100%"
34676
34687
  },
34677
34688
  title: "Picobot"
@@ -35400,6 +35411,7 @@ function PicobotComponent({
35400
35411
  closeDrawer,
35401
35412
  currentPath,
35402
35413
  env,
35414
+ isOpen,
35403
35415
  onChangePath,
35404
35416
  openTool,
35405
35417
  pending
@@ -35444,6 +35456,17 @@ function PicobotComponent({
35444
35456
  }
35445
35457
  clearPending();
35446
35458
  }, [pending, isPicobotReady, clearPending]);
35459
+ // The drawer keeps the iframe mounted while it's closed, so picobot's
35460
+ // autofocus is spent by the time the panel reopens. READY gates the signal
35461
+ // because picobot has no listener before it.
35462
+ React__default.useEffect(function tellPicobotThePanelIsVisible() {
35463
+ var _a;
35464
+ if (isOpen && isPicobotReady) {
35465
+ (_a = picobotRef.current) === null || _a === void 0 ? void 0 : _a.postMessage({
35466
+ type: "PANE_VISIBLE"
35467
+ });
35468
+ }
35469
+ }, [isOpen, isPicobotReady]);
35447
35470
  const [iframeErrored, setIframeErrored] = React__default.useState(false);
35448
35471
  const [mountKey, setMountKey] = React__default.useState(0);
35449
35472
  const handleRetry = React__default.useCallback(() => {
@@ -35960,6 +35983,11 @@ const Profile = {
35960
35983
  };
35961
35984
 
35962
35985
  const DURATION = 100;
35986
+ const TOOLBAR_DRAWER_CHANGE_EVENT = "topbar:toolbar-drawer-change";
35987
+ const CLOSED_TOOLBAR_DRAWER = {
35988
+ open: false,
35989
+ tool: null
35990
+ };
35963
35991
  // Exempt paths match regardless of query, so `email?conversationId=…` is
35964
35992
  // exempt along with `email`.
35965
35993
  function isToolPathExemptFromStorage(toolName, path) {
@@ -36073,6 +36101,27 @@ function Toolbar({
36073
36101
  delete window.openHelpdesk; /* TODO: Remove when products have migrated to window.topbar.openHelpdesk */
36074
36102
  };
36075
36103
  }, [Profile, openHelpdeskAtRoot]);
36104
+ const toolbarDrawerTool = open && currentContent ? currentContent.name.toLowerCase() : null;
36105
+ const lastToolbarDrawer = React__default.useRef(CLOSED_TOOLBAR_DRAWER);
36106
+ React__default.useEffect(() => {
36107
+ const toolbarDrawer = {
36108
+ open,
36109
+ tool: toolbarDrawerTool
36110
+ };
36111
+ window.topbar = window.topbar || {};
36112
+ window.topbar.toolbarDrawer = toolbarDrawer;
36113
+ const last = lastToolbarDrawer.current;
36114
+ if (last.open !== toolbarDrawer.open || last.tool !== toolbarDrawer.tool) {
36115
+ lastToolbarDrawer.current = toolbarDrawer;
36116
+ window.dispatchEvent(new CustomEvent(TOOLBAR_DRAWER_CHANGE_EVENT, {
36117
+ detail: toolbarDrawer
36118
+ }));
36119
+ }
36120
+ return () => {
36121
+ var _a;
36122
+ (_a = window.topbar) === null || _a === void 0 ? true : delete _a.toolbarDrawer;
36123
+ };
36124
+ }, [open, toolbarDrawerTool]);
36076
36125
  const loginSettingsUrl = getLoginProfilePath({
36077
36126
  appName: appName.replace(/[\s-]/, ""),
36078
36127
  env: env,