{"version":3,"file":"createFullscreenModeHost.cjs","names":["_createStore","require","_createBrowserMessageBus","_fullscreenMessageChannel","_fullscreenState","messageCreators","fullscreenMessageChannel","createFullscreenModeHost","fullscreenMessageBus","createBrowserMessageBus","messageChannel","keyWindow","window","store","createStore","fullscreenStack","getFullscreenStack","getState","publishFullscreenStack","publish","stateChanged","updateFullscreenStack","nextFullscreenStack","getIsSameFullscreenStack","currentFullscreenStack","setState","unsubscribeFromFullscreenMessages","subscribe","fullscreenMessage","type","getStackWithEnteredId","fullscreenId","payload","getStackWithExitedId","length","handleKeyDown","keyboardEvent","activeFullscreenId","getActiveFullscreenId","key","defaultPrevented","isComposing","undefined","preventDefault","exitRequested","addEventListener","stateRequested","destroy","removeEventListener","exports"],"sources":["../../../../src/message-bus/fullscreen/createFullscreenModeHost.ts"],"sourcesContent":["/*!\n * Copyright (c) 2026-present, Okta, Inc. and/or its affiliates. All rights reserved.\n * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the \"License.\")\n *\n * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *\n * See the License for the specific language governing permissions and limitations under the License.\n */\n\nimport { type MessageBus } from \"../../tools/createMessageBus.js\";\nimport { createStore } from \"../../tools/createStore.js\";\nimport { createBrowserMessageBus } from \"../createBrowserMessageBus.js\";\nimport {\n  type FullscreenMessage,\n  fullscreenMessageChannel,\n} from \"./fullscreenMessageChannel.js\";\nimport {\n  getActiveFullscreenId,\n  getIsSameFullscreenStack,\n  getStackWithEnteredId,\n  getStackWithExitedId,\n} from \"./fullscreenState.js\";\n\nconst { messageCreators } = fullscreenMessageChannel;\n\nexport type FullscreenModeHost = {\n  /**\n   * Stop listening, and clear the stack, so fullscreen never outlives the page part that owned it. UI Shell calls this when it unmounts.\n   */\n  destroy: () => void;\n};\n\nexport type CreateFullscreenModeHostArgs = {\n  /**\n   * Carries fullscreen messages between every app on the page.\n   * @default a `createBrowserMessageBus` on `fullscreenMessageChannel`\n   */\n  fullscreenMessageBus?: MessageBus<FullscreenMessage>;\n  /**\n   * The window to listen on for Escape.\n   * @default window\n   */\n  keyWindow?: Window;\n};\n\n/**\n * Create the fullscreen mode host: the one place on the page that owns the stack of fullscreen ids and answers every app's requests.\n *\n * Unified UI Shell runs one for you, because only one shell renders on a page while any number of apps can. Call this yourself only in an app that renders without UI Shell. Rendering the shell's parts without `renderUiShell` loses more than fullscreen, so prefer the shell.\n *\n * Escape removes the top id, so each press goes back one step. A dialog, menu, or popover that handles Escape first wins, because it stops the keypress or marks it handled before it reaches `window`. Screen reader users in browse mode don't send Escape to the page, so fullscreen never exits under them by surprise; the fullscreen button is how they leave.\n *\n * Two hosts on one page, such as two shells on a Storybook docs page, stay in step: each one adopts every stack the other sends. A host with nothing in fullscreen doesn't answer `stateRequested`, so a host that starts late can never send an empty stack over a real one.\n *\n * @experimental The fullscreen API is in alpha and may change in a future release.\n */\nexport const createFullscreenModeHost = ({\n  fullscreenMessageBus = createBrowserMessageBus({\n    messageChannel: fullscreenMessageChannel,\n  }),\n  keyWindow = window,\n}: CreateFullscreenModeHostArgs = {}): FullscreenModeHost => {\n  const store = createStore<{ fullscreenStack: string[] }>({\n    fullscreenStack: [],\n  });\n\n  const getFullscreenStack = () => store.getState(\"fullscreenStack\") ?? [];\n\n  const publishFullscreenStack = (fullscreenStack: string[]) => {\n    fullscreenMessageBus.publish(\n      messageCreators.stateChanged({ fullscreenStack }),\n    );\n  };\n\n  const updateFullscreenStack = (nextFullscreenStack: string[]) => {\n    if (\n      !getIsSameFullscreenStack({\n        currentFullscreenStack: getFullscreenStack(),\n        nextFullscreenStack,\n      })\n    ) {\n      store.setState(\"fullscreenStack\", nextFullscreenStack);\n      publishFullscreenStack(nextFullscreenStack);\n    }\n  };\n\n  const unsubscribeFromFullscreenMessages = fullscreenMessageBus.subscribe(\n    (fullscreenMessage) => {\n      if (fullscreenMessage.type === \"fullscreen/enterRequested\") {\n        updateFullscreenStack(\n          getStackWithEnteredId({\n            fullscreenId: fullscreenMessage.payload.fullscreenId,\n            fullscreenStack: getFullscreenStack(),\n          }),\n        );\n      }\n\n      if (fullscreenMessage.type === \"fullscreen/exitRequested\") {\n        updateFullscreenStack(\n          getStackWithExitedId({\n            fullscreenId: fullscreenMessage.payload.fullscreenId,\n            fullscreenStack: getFullscreenStack(),\n          }),\n        );\n      }\n\n      if (fullscreenMessage.type === \"fullscreen/exitAllRequested\") {\n        updateFullscreenStack([]);\n      }\n\n      if (\n        fullscreenMessage.type === \"fullscreen/stateRequested\" &&\n        getFullscreenStack().length > 0\n      ) {\n        publishFullscreenStack(getFullscreenStack());\n      }\n\n      // Adopting without publishing is what keeps two hosts from answering each other forever. A host's own message arrives here too and changes nothing.\n      if (fullscreenMessage.type === \"fullscreen/stateChanged\") {\n        store.setState(\n          \"fullscreenStack\",\n          fullscreenMessage.payload.fullscreenStack,\n        );\n      }\n    },\n  );\n\n  const handleKeyDown = (keyboardEvent: KeyboardEvent) => {\n    const activeFullscreenId = getActiveFullscreenId({\n      fullscreenStack: getFullscreenStack(),\n    });\n\n    if (\n      keyboardEvent.key === \"Escape\" &&\n      !keyboardEvent.defaultPrevented &&\n      !keyboardEvent.isComposing &&\n      activeFullscreenId !== undefined\n    ) {\n      keyboardEvent.preventDefault();\n\n      // Marking the keypress handled is what keeps a second host on the page from also acting on it and removing the id under this one. Sent as a request rather than applied here, so both hosts remove the same id.\n      fullscreenMessageBus.publish(\n        messageCreators.exitRequested({ fullscreenId: activeFullscreenId }),\n      );\n    }\n  };\n\n  keyWindow.addEventListener(\"keydown\", handleKeyDown);\n\n  fullscreenMessageBus.publish(messageCreators.stateRequested({}));\n\n  const destroy = () => {\n    keyWindow.removeEventListener(\"keydown\", handleKeyDown);\n    unsubscribeFromFullscreenMessages();\n\n    if (getFullscreenStack().length > 0) {\n      store.setState(\"fullscreenStack\", []);\n      publishFullscreenStack([]);\n    }\n  };\n\n  return { destroy };\n};\n"],"mappings":"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","ignoreList":[]}