"use strict"; Object.defineProperty(exports, "__esModule", { value: !0 }); var jsxRuntime = require("react/jsx-runtime"), ui = require("@sanity/ui"), react = require("react"), lodashIsEqual = require("lodash/isEqual"), pako = require("pako"), client = require("react-dom/client"), styled = require("styled-components"), lodashDebounce = require("lodash/debounce"), icons = require("@sanity/icons"), segmentedProperty = require("segmented-property"); function _interopDefaultCompat(e) { return e && typeof e == "object" && "default" in e ? e : { default: e }; } var lodashIsEqual__default = /* @__PURE__ */ _interopDefaultCompat(lodashIsEqual), pako__default = /* @__PURE__ */ _interopDefaultCompat(pako), styled__default = /* @__PURE__ */ _interopDefaultCompat(styled), lodashDebounce__default = /* @__PURE__ */ _interopDefaultCompat(lodashDebounce); function defineConfig(config) { return config; } function defineRuntime(config) { return config; } function defineScope(scope) { return scope; } const EMPTY_ARRAY = [], EMPTY_RECORD = {}, DEFAULT_VIEWPORT_VALUE = "auto", DEFAULT_ZOOM_VALUE = 1, VIEWPORT_OPTIONS = [ { name: "auto", title: "Full", rect: { width: "auto" } }, { name: "768", title: "768px", rect: { width: 768 } }, { name: "375", title: "375px", rect: { width: 375, height: 667 } }, { name: "320", title: "320px", rect: { width: 320, height: 568 } } ], ZOOM_OPTIONS = [ { value: 0.5, title: "50%" }, { value: 0.75, title: "75%" }, { value: 1, title: "100%" }, { value: 1.5, title: "150%" }, { value: 2, title: "200%" }, { value: 3, title: "300%" } ]; function createPubsub() { const subscribers = /* @__PURE__ */ new Set(); return { publish(msg) { for (const subscriber of subscribers) subscriber(msg); }, subscribe(subscriber) { return subscribers.add(subscriber), () => { subscribers.delete(subscriber); }; } }; } const qs = { parse(str) { const params = new URLSearchParams("?" + str), q = {}; return params.forEach((value, key) => { q[key] = value; }), q; }, stringify(q) { return Object.entries(q).map(([key, value]) => `${key}=${value}`).join("&"); } }; function resolveLocation(scopes, path) { const segments = path.split("/").slice(1).filter(Boolean), p = segments.join("/"); if (segments.length === 0) return { scope: null, story: null }; for (const scope of scopes) for (const story of scope.stories) { const storyPath = [scope.name, story.name].filter(Boolean).join("/"); if (p === storyPath) return { scope, story }; } return { scope: null, story: null }; } function buildFrameUrl(params) { const { baseUrl = "/frame/", path, payload, scheme, viewport, zoom } = params; return [ baseUrl, `?path=${encodeURIComponent(path)}`, `&scheme=${scheme}`, `&viewport=${viewport}`, `&zoom=${zoom}`, ...Object.entries(payload).map(([key, value]) => `&${key}=${value}`) ].join(""); } const PropsContext = react.createContext(null); function useProps() { const props = react.useContext(PropsContext); if (!props) throw new Error("Props: missing context value"); return props; } const BooleanProp = react.memo(function(props) { const { schema, value } = props, { setPropValue } = useProps(); return /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { as: "label", padding: 3, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginRight: 2, style: { lineHeight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx( ui.Checkbox, { checked: value || !1, onChange: (event) => setPropValue(schema.name, event.currentTarget.checked) } ) }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { paddingY: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: schema.name }) }) ] }); }), NumberProp = react.memo(function(props) { const { schema, value = "" } = props, { setPropValue } = useProps(); return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { padding: 3, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: schema.name }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx( ui.TextInput, { fontSize: [2, 2, 1], onChange: (event) => setPropValue(schema.name, Number(event.currentTarget.value)), padding: 2, value } ) }) ] }); }), SelectProp = react.memo(function(props) { const { schema, value: valueProp } = props, { setPropValue } = useProps(), value = react.useMemo(() => { const entries = Object.entries(schema.options); for (const [k, v] of entries) if (v === valueProp) return k; return ""; }, [schema, valueProp]); return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { padding: 3, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: schema.name }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx( ui.Select, { fontSize: [2, 2, 1], onChange: (event) => { const optionKey = event.currentTarget.value, optionValue = schema.options[optionKey]; setPropValue(schema.name, optionValue); }, padding: 2, radius: 2, value: String(value || ""), children: Object.entries(schema.options).map(([key]) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: key, children: key }, key)) } ) }) ] }); }), StringProp = react.memo(function(props) { const { schema, value } = props, { setPropValue } = useProps(); return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { padding: 3, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: schema.name }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx( ui.TextInput, { fontSize: [2, 2, 1], onChange: (event) => setPropValue(schema.name, event.currentTarget.value), padding: 2, value: value || "" } ) }) ] }); }), TextProp = react.memo(function(props) { const { schema, value } = props, { setPropValue } = useProps(); return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { padding: 3, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, weight: "semibold", children: schema.name }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { marginTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx( ui.TextArea, { fontSize: [2, 2, 1], onChange: (event) => setPropValue(schema.name, event.currentTarget.value), rows: 4, value: value || "" } ) }) ] }); }); function Prop(props) { const { schema, value } = props; return schema.type === "boolean" ? /* @__PURE__ */ jsxRuntime.jsx(BooleanProp, { schema, value }) : schema.type === "number" ? /* @__PURE__ */ jsxRuntime.jsx(NumberProp, { schema, value }) : schema.type === "select" ? /* @__PURE__ */ jsxRuntime.jsx(SelectProp, { schema, value }) : schema.type === "string" ? /* @__PURE__ */ jsxRuntime.jsx(StringProp, { schema, value }) : schema.type === "text" ? /* @__PURE__ */ jsxRuntime.jsx(TextProp, { schema, value }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: 1, weight: "semibold", children: [ "Unknown Prop type:", " ", /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [ schema.name, ": ", schema.type ] }) ] }) }); } const PropsInspector = react.memo(function() { const { schemas, value } = useProps(); return /* @__PURE__ */ jsxRuntime.jsxs(ui.Box, { padding: 2, children: [ schemas.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, size: [2, 2, 1], children: "No properties" }) }), schemas.length > 0 && schemas.map((schema, schemaIndex) => /* @__PURE__ */ jsxRuntime.jsx( Prop, { schema, value: value[schema.name] === void 0 ? schema.defaultValue : value[schema.name] }, schemaIndex )) ] }); }), isEqual = lodashIsEqual__default.default; function useUnique(value) { const valueRef = react.useRef(value); return _isEqual(valueRef.current, value) || (valueRef.current = value), valueRef.current; } function _isEqual(objA, objB) { if (!objA || !objB) return objA === objB; const keysA = Object.keys(objA), keysB = Object.keys(objB); return keysA.length !== keysB.length ? !1 : keysA.every((key) => objA[key] === objB[key]); } const WorkshopContext = react.createContext(null); function useWorkshop() { const workshop = react.useContext(WorkshopContext); if (!workshop) throw new Error("Workshop: missing context value"); return workshop; } const btoa = typeof window > "u" ? (str) => Buffer.from(str, "binary").toString("base64") : window.btoa, atob = typeof window > "u" ? (str) => Buffer.from(str, "base64").toString("binary") : window.atob; function uint8ArrayToBase64(uint8array) { let str = ""; for (let i = 0, { length } = uint8array; i < length; i++) str += String.fromCharCode(uint8array[i]); return btoa(str); } function base64ToUint8Array(base64) { const binStr = atob(base64), len = binStr.length, bytes = new Uint8Array(len); for (let i = 0; i < len; i++) bytes[i] = binStr.charCodeAt(i); return bytes; } function decode(input) { if (input.length === 0) return ""; const arr = base64ToUint8Array(input); return pako__default.default.inflate(arr, { to: "string" }); } function encode(input) { if (input.length === 0) return ""; const arr = pako__default.default.deflate(input); return uint8ArrayToBase64(arr); } function encodeValue(val) { return encode(JSON.stringify(val)); } function decodeValue(val) { try { return JSON.parse(decode(val)); } catch { return EMPTY_RECORD; } } function propsReducer(state, msg) { return msg.type === "workshop/props/setValue" ? state.value === msg.value ? state : { ...state, value: msg.value } : msg.type === "workshop/props/registerProp" ? state.schemas.some((s) => s.name === msg.schema.name) ? state : { ...state, schemas: state.schemas.concat([msg.schema]) } : msg.type === "workshop/props/setPropValue" ? state.value[msg.name] === msg.value ? state : { ...state, value: { ...state.value, [msg.name]: msg.value } } : state; } const PropsProvider = react.memo(function(props) { const { children } = props, { channel, broadcast, payload } = useWorkshop(), encodedValue = payload.value, encodedValueRef = react.useRef(encodedValue), [{ schemas, value: unstable_value }, setState] = react.useState(() => ({ schemas: EMPTY_ARRAY, value: decodeValue(String(encodedValue)) })), value = useUnique(unstable_value), registerProp = react.useCallback( (schema) => { broadcast({ type: "workshop/props/registerProp", schema }); }, [broadcast] ), unregisterProp = react.useCallback( (name) => { broadcast({ type: "workshop/props/unregisterProp", name }); }, [broadcast] ), setPropValue = react.useCallback( (name, _value) => { broadcast({ type: "workshop/props/setPropValue", name, value: _value }); }, [broadcast] ), ctx = react.useMemo( () => ({ registerProp, schemas, setPropValue, unregisterProp, value }), [registerProp, schemas, setPropValue, unregisterProp, value] ); return react.useEffect( () => channel.subscribe((msg) => { setState((prevState) => { const nextState = msg.type === "workshop/setPath" ? { schemas: EMPTY_ARRAY, value: EMPTY_RECORD } : propsReducer(prevState, msg); return isEqual(prevState, nextState) ? prevState : nextState; }); }), [channel] ), react.useEffect(() => { const nextEncodedValue = encodeValue(value); encodedValueRef.current !== nextEncodedValue && (encodedValueRef.current = nextEncodedValue, broadcast({ type: "workshop/setPayloadValue", key: "value", value: nextEncodedValue })); }, [broadcast, value]), react.useEffect(() => { encodedValueRef.current !== encodedValue && (encodedValueRef.current = encodedValue, setState((prevState) => ({ ...prevState, value: decodeValue(String(encodedValue)) || {} }))); }, [encodedValue]), /* @__PURE__ */ jsxRuntime.jsx(PropsContext.Provider, { value: ctx, children }); }); function useBoolean(name, defaultValue, groupName = "Props") { const { registerProp, unregisterProp, value } = useProps(); return react.useEffect(() => (registerProp({ type: "boolean", groupName, name, defaultValue }), () => unregisterProp(name)), [defaultValue, groupName, name, registerProp, unregisterProp]), value[name] === void 0 ? defaultValue : value[name]; } function useNumber(name, defaultValue, groupName = "Props") { const { registerProp, unregisterProp, value } = useProps(); return react.useEffect(() => (registerProp({ type: "number", groupName, name, defaultValue }), () => unregisterProp(name)), [defaultValue, groupName, name, registerProp, unregisterProp]), value[name] === void 0 ? defaultValue : value[name]; } function useSelect(name, options, defaultValue, groupName = "Props") { const { registerProp, unregisterProp, value } = useProps(); return react.useEffect(() => (registerProp({ type: "select", groupName, name, options, defaultValue }), () => unregisterProp(name)), [defaultValue, groupName, name, options, registerProp, unregisterProp]), value[name] === void 0 ? defaultValue : value[name]; } function useString(name, defaultValue, groupName = "Props") { const { registerProp, unregisterProp, value } = useProps(); return react.useEffect(() => (registerProp({ type: "string", groupName, name, defaultValue }), () => unregisterProp(name)), [defaultValue, groupName, name, registerProp, unregisterProp]), value[name] === void 0 ? defaultValue : value[name]; } function useText(name, defaultValue, groupName = "Props") { const { registerProp, unregisterProp, value } = useProps(); return react.useEffect(() => (registerProp({ type: "text", groupName, name, defaultValue }), () => unregisterProp(name)), [defaultValue, groupName, name, registerProp, unregisterProp]), value[name] === void 0 ? defaultValue : value[name]; } function propsPlugin() { return { name: "props", title: "Properties", inspector: PropsInspector, provider: PropsProvider }; } const WorkshopProvider = react.memo(function(props) { const { broadcast, children, channel, config, frameReady, origin, path, payload, scheme, viewport = "auto", zoom = 1 } = props, { plugins: pluginsProp = EMPTY_ARRAY, collections = EMPTY_ARRAY, frameUrl = "/frame/", scopes, title = "Workshop" } = config; if (!payload) throw new Error("missing `payload` property"); const plugins = react.useMemo(() => [propsPlugin(), ...pluginsProp], [pluginsProp]), { scope, story } = react.useMemo(() => resolveLocation(scopes, path), [path, scopes]), workshop = react.useMemo( () => ({ plugins, broadcast, channel, collections, frameReady, frameUrl, origin, path, payload, scheme, scope, scopes, story, title, viewport, zoom }), [ plugins, broadcast, channel, collections, frameReady, frameUrl, origin, path, payload, scheme, scope, scopes, story, title, viewport, zoom ] ); let wrappedChildren = children; for (const plugin of plugins) plugin.provider && (wrappedChildren = react.createElement( plugin.provider, { options: plugin.options || EMPTY_RECORD }, wrappedChildren )); return /* @__PURE__ */ jsxRuntime.jsx(WorkshopContext.Provider, { value: workshop, children: wrappedChildren }); }); function workshopReducer(state, msg) { if (msg.type === "workshop/frameReady") return { ...state, frameReady: !0 }; if (msg.type === "workshop/setState") return isEqual(state, msg.value) ? state : msg.value; if (msg.type === "workshop/setZoom") return state.zoom === msg.value ? state : { ...state, zoom: msg.value }; if (msg.type === "workshop/setViewport") return state.viewport === msg.value ? state : { ...state, viewport: msg.value }; if (msg.type === "workshop/toggleScheme") return { ...state, scheme: state.scheme === "light" ? "dark" : "light" }; if (msg.type === "workshop/setScheme") return state.scheme === msg.value ? state : { ...state, scheme: msg.value }; if (msg.type === "workshop/setPath") return state.path === msg.value ? state : { ...state, path: msg.value }; if (msg.type === "workshop/setPayload") return isEqual(state.payload, msg.value) ? state : { ...state, payload: msg.value }; if (msg.type === "workshop/setPayloadValue") { const payload = { ...state.payload, [msg.key]: msg.value }; return isEqual(state.payload, payload) ? state : { ...state, payload }; } return state; } const ROOT_PATH = (() => { try { return process.env.ROOT_PATH; } catch { return; } })(), RE_URL = /http:\/\/([^:/\s]+)(:[0-9]+)?/g, RE_VITE_FS_PREFIX = /\/@fs\//g, RE_VITE_FS_SUFFIX = /\?([a-z]{1})=([0-9]+)/g; function formatStack(stack) { let ret = decodeURIComponent(stack); return ret = stack.replace(RE_URL, "").replace(RE_VITE_FS_PREFIX, "/").replace(RE_VITE_FS_SUFFIX, ""), ROOT_PATH ? replaceRootPath(ret, ROOT_PATH + "/") : ret; } function replaceRootPath(str, rootPath) { const re = new RegExp(rootPath.replace(/\//g, "\\/"), "g"); return str.replace(re, ""); } const WorkshopCanvas$1 = react.memo(function() { const { story } = useWorkshop(), [state, setState] = react.useState({ error: null, errorInfo: null }), catchError = react.useCallback( ({ error, info: errorInfo }) => { setState({ error, errorInfo }); }, [] ), handleRetry = react.useCallback(() => { setState({ error: null, errorInfo: null }); }, []); return story ? state.error ? /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { as: "main", height: "fill", overflow: "auto", tone: "critical", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorScreen, { error: state.error, errorInfo: state.errorInfo, onRetry: handleRetry }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsx("h1", { hidden: !0, children: story.title }), /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(LoadingScreen, { story }), children: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { as: "main", height: "fill", children: /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorBoundary, { onCatch: catchError, children: react.createElement(story.component) }) }) }) ] }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, {}); }), LoadingScreen = react.memo(function(props) { const { story } = props; return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsxs("h1", { hidden: !0, children: [ "Loading ", /* @__PURE__ */ jsxRuntime.jsx("em", { children: story.title }), "\u2026" ] }), /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { align: "center", as: "main", height: "fill", justify: "center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { muted: !0 }) }) ] }); }), ErrorScreen = react.memo(function(props) { const { error, errorInfo, onRetry } = props; return /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 4, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { as: "h1", size: [1, 1, 2], children: error.message }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onRetry, text: "Retry" }) }), error.stack && /* @__PURE__ */ jsxRuntime.jsx(ui.Code, { size: 1, children: formatStack(error.stack) }), (errorInfo == null ? void 0 : errorInfo.componentStack) && /* @__PURE__ */ jsxRuntime.jsx(ui.Code, { size: 1, children: "Component stack:" + formatStack(errorInfo.componentStack) }) ] }) }); }); function isArray(value) { return Array.isArray(value); } function isRecord(value) { return !!value && typeof value == "object" && !Array.isArray(value); } function createMainController() { const _subscribers = /* @__PURE__ */ new Set(); let _msgQueue = [], _flushTimeout = null; function _flush() { _flushTimeout && clearInterval(_flushTimeout), _flushTimeout = setTimeout(() => { window.parent.postMessage(_msgQueue), _msgQueue = [], _flushTimeout = null; }, 0); } function _handleMessage(event) { const msgs = event.data; if (isArray(msgs)) { for (const msg of msgs) if (isRecord(msg) && typeof msg.type == "string" && msg.type.startsWith("workshop/")) for (const subscriber of _subscribers) subscriber(msg); } } function _mount() { window.addEventListener("message", _handleMessage, !1); } function _unmount() { window.removeEventListener("message", _handleMessage, !1); } return { message: { publish(msg) { _msgQueue.push(msg), _flush(); }, subscribe(subscriber) { return _subscribers.add(subscriber), _subscribers.size === 1 && _mount(), () => { _subscribers.delete(subscriber), _subscribers.size === 0 && _unmount(); }; } } }; } function getStateFromLocation$1() { const query = typeof window > "u" ? {} : qs.parse(window.location.search.slice(1)), { path = "/", scheme, viewport, zoom, ...payload } = query; return { frameReady: !1, path, payload, scheme: typeof scheme == "string" ? scheme : "light", viewport: typeof viewport == "string" ? viewport : "auto", zoom: typeof zoom == "string" ? Number(zoom) : 1 }; } const WorkshopFrame = react.memo(function(props) { const { config, setScheme } = props, main = react.useMemo(() => createMainController(), []), channel = react.useMemo(() => createPubsub(), []), [boundaryElement, setBoundaryElement] = react.useState(null), [portalElement, setPortalElement] = react.useState(null), broadcast = react.useCallback( (msg) => { channel.publish(msg), main.message.publish(msg); }, [channel, main] ), [{ frameReady, path, payload, scheme, viewport, zoom }, setState] = react.useState( () => getStateFromLocation$1() ); return react.useEffect(() => channel.subscribe((msg) => setState((s) => workshopReducer(s, msg))), [channel]), react.useEffect(() => main.message.subscribe(channel.publish), [channel, main]), react.useEffect(() => setScheme(scheme), [setScheme, scheme]), react.useEffect(() => broadcast({ type: "workshop/frameReady" }), [broadcast]), /* @__PURE__ */ jsxRuntime.jsx(ui.ToastProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.BoundaryElementProvider, { element: boundaryElement, children: /* @__PURE__ */ jsxRuntime.jsx(ui.PortalProvider, { element: portalElement, children: /* @__PURE__ */ jsxRuntime.jsx( WorkshopProvider, { broadcast, config, channel, frameReady, origin: "frame", path, payload, scheme, viewport, zoom, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, { height: "fill", ref: setBoundaryElement, children: [ /* @__PURE__ */ jsxRuntime.jsx(WorkshopCanvas$1, {}), /* @__PURE__ */ jsxRuntime.jsx("div", { "data-portal": "", ref: setPortalElement }) ] }) } ) }) }) }); }); function _buildLocationUrl(loc) { const search = qs.stringify(loc.query || {}); return `${loc.path}${search ? `?${search}` : ""}`; } function _getStateFromWindow() { return { path: location.pathname, query: qs.parse(location.search.substr(1)) }; } function createLocationStore() { const _subscribers = /* @__PURE__ */ new Set(); function _handlePopState() { _notifySubscribers({ type: "pop", ..._getStateFromWindow() }); } function _notifySubscribers(loc) { for (const subscriber of _subscribers) subscriber(loc); } function _mount() { window.addEventListener("popstate", _handlePopState); } function _unmount() { window.removeEventListener("popstate", _handlePopState); } return { get() { return _getStateFromWindow(); }, push(nextLocation) { window.history.pushState(null, document.title, _buildLocationUrl(nextLocation)), _notifySubscribers({ type: "push", ...nextLocation }); }, replace(nextLocation) { window.history.replaceState(null, document.title, _buildLocationUrl(nextLocation)), _notifySubscribers({ type: "replace", ...nextLocation }); }, subscribe(subscribe) { return _subscribers.add(subscribe), _subscribers.size === 1 && _mount(), () => { _subscribers.delete(subscribe), _subscribers.size === 0 && _unmount(); }; } }; } const GlobalStyle = styled.createGlobalStyle` @font-face { font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap; src: url('https://rsms.me/inter/font-files/Inter-roman.var.woff2?v=3.19') format('woff2'); font-named-instance: 'Regular'; } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 100 900; font-display: swap; src: url('https://rsms.me/inter/font-files/Inter-italic.var.woff2?v=3.19') format('woff2'); font-named-instance: 'Italic'; } body { background-color: ${({ theme }) => theme.sanity.color.base.bg}; } `, MemoTab = react.memo(ui.Tab), Root$4 = styled__default.default(ui.Card)` line-height: 0; @media screen and (max-width: ${({ theme }) => theme.sanity.media[1] - 1}px) { text-align: center; } `, InspectorHeader = react.memo(function(props) { const { currentTabId, onTabChange, tabs } = props, layerStyle = react.useMemo(() => ({ flex: "none", position: "sticky", top: 0 }), []), children = react.useMemo( () => tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx( InspectorTabView, { onTabChange, selected: tab.id === currentTabId, tab }, tab.id )), [currentTabId, onTabChange, tabs] ); return /* @__PURE__ */ jsxRuntime.jsx(ui.Layer, { style: layerStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Root$4, { padding: 2, shadow: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ui.TabList, { space: 1, children }) }) }); }); function InspectorTabView(props) { const { onTabChange, selected, tab } = props, handleClick = react.useCallback(() => { onTabChange(tab.id); }, [onTabChange, tab]); return /* @__PURE__ */ jsxRuntime.jsx( MemoTab, { "aria-controls": `${tab.id}-panel`, fontSize: [2, 2, 1], id: tab.id, label: tab.label, onClick: handleClick, selected, tone: tab.tone } ); } const Root$3 = styled__default.default(ui.Card)` overflow: hidden; @media screen and (min-width: ${({ theme }) => theme.sanity.media[1]}px) { border-left: 1px solid var(--card-border-color); min-width: 180px; max-width: 300px; overflow: auto; } `, MemoRender = react.memo(function(props) { return react.createElement(props.component, { options: props.options }); }), WorkshopInspector = react.memo(function(props) { const { expanded } = props, { plugins } = useWorkshop(), tabs = react.useMemo(() => plugins.filter((plugin) => plugin.inspector).map((plugin) => ({ id: plugin.name, label: plugin.title, tone: void 0, plugin })), [plugins]), [tabId, setTabId] = react.useState(tabs.length > 0 ? tabs[0].id : null), currentTab = tabs.find((tab) => tab.id === tabId), showTabs = tabs.length > 1, display = react.useMemo( () => expanded ? ["block"] : ["none", "none", "block"], [expanded] ); return /* @__PURE__ */ jsxRuntime.jsx(Root$3, { display, flex: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", height: "fill", children: [ showTabs && /* @__PURE__ */ jsxRuntime.jsx(InspectorHeader, { currentTabId: tabId, onTabChange: setTabId, tabs }), showTabs && tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx( ui.TabPanel, { "aria-labelledby": `${tab.id}-tab`, flex: 1, hidden: tab.id !== tabId, id: `${tab.id}-panel`, overflow: "auto", children: tab.plugin.inspector && /* @__PURE__ */ jsxRuntime.jsx( MemoRender, { component: tab.plugin.inspector, options: tab.plugin.options || EMPTY_RECORD } ) }, tab.id )), !showTabs && (currentTab == null ? void 0 : currentTab.plugin.inspector) && /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { flex: 1, overflow: "auto", children: /* @__PURE__ */ jsxRuntime.jsx( MemoRender, { component: currentTab.plugin.inspector, options: currentTab.plugin.options || EMPTY_RECORD } ) }) ] }) }); }), debounce = lodashDebounce__default.default; function NavbarBreadcrumbs() { const { broadcast, scope, story, title } = useWorkshop(), handleHomeClick = react.useCallback( (event) => { event.preventDefault(), broadcast({ type: "workshop/setPath", value: "/" }); }, [broadcast] ); return /* @__PURE__ */ jsxRuntime.jsx( NavbarBreadcrumbsView, { onHomeClick: handleHomeClick, scopeTitle: scope == null ? void 0 : scope.title, storyTitle: story == null ? void 0 : story.title, title } ); } const NavbarBreadcrumbsView = react.memo(function(props) { const { onHomeClick, scopeTitle, storyTitle, title } = props; return /* @__PURE__ */ jsxRuntime.jsxs( ui.Breadcrumbs, { separator: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { muted: !0, size: [2, 2, 1], children: "/" }), space: 2, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: [2, 2, 1], weight: "bold", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/", onClick: onHomeClick, style: { color: "inherit" }, children: title }) }), scopeTitle && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { align: "center", size: [2, 2, 1], children: scopeTitle }), storyTitle && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: [2, 2, 1], children: storyTitle }) ] } ); }), OpenCanvasButton = react.memo(function() { const { frameUrl, path, payload, scheme, zoom, viewport } = useWorkshop(), canvasUrl = react.useMemo( () => path === "/" ? void 0 : buildFrameUrl({ baseUrl: frameUrl, path, payload, scheme, zoom, viewport }), [frameUrl, path, payload, scheme, zoom, viewport] ); return /* @__PURE__ */ jsxRuntime.jsx( ui.Button, { as: canvasUrl ? "a" : "button", disabled: !canvasUrl, fontSize: 1, href: canvasUrl, iconRight: icons.LaunchIcon, mode: "ghost", padding: 2, rel: "noopener noreferrer", target: "_blank", text: "Open story" } ); }); function SchemeMenu() { const { broadcast, scheme } = useWorkshop(), handleToggleScheme = react.useCallback(() => { broadcast({ type: "workshop/toggleScheme" }); }, [broadcast]); return /* @__PURE__ */ jsxRuntime.jsx(SchemeMenuView, { dark: scheme === "dark", onToggleScheme: handleToggleScheme }); } const SchemeMenuView = react.memo(function(props) { const { dark, onToggleScheme } = props; return /* @__PURE__ */ jsxRuntime.jsx( ui.Button, { fontSize: 1, icon: dark ? icons.MoonIcon : icons.SunIcon, mode: "bleed", onClick: onToggleScheme, padding: 2 } ); }), ViewportMenu = react.memo(function() { const { broadcast, story, viewport } = useWorkshop(), setViewport = react.useCallback( (value) => { broadcast({ type: "workshop/setViewport", value }); }, [broadcast] ); return /* @__PURE__ */ jsxRuntime.jsx(ViewportMenuView, { disabled: !story, setViewport, viewport }); }), POPOVER_PROPS$1 = { constrainSize: !0, placement: "bottom", portal: !0 }, ViewportMenuView = react.memo(function(props) { var _a; const { disabled, setViewport, viewport } = props; return /* @__PURE__ */ jsxRuntime.jsx( ui.MenuButton, { button: /* @__PURE__ */ jsxRuntime.jsx( ui.Button, { disabled, fontSize: 1, iconRight: icons.SelectIcon, mode: "ghost", padding: 2, text: (_a = VIEWPORT_OPTIONS.find((o) => o.name === viewport)) == null ? void 0 : _a.title } ), id: "viewport-menu", menu: /* @__PURE__ */ jsxRuntime.jsx(ui.Menu, { children: VIEWPORT_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx( ui.MenuItem, { fontSize: 1, onClick: () => setViewport(option.name), padding: 2, selected: option.name === viewport, text: option.title }, option.name )) }), popover: POPOVER_PROPS$1 } ); }); function ZoomMenu() { const { broadcast, story, zoom } = useWorkshop(), setZoom = react.useCallback( (value) => broadcast({ type: "workshop/setZoom", value }), [broadcast] ); return /* @__PURE__ */ jsxRuntime.jsx(ZoomMenuView, { disabled: !story, setZoom, zoom }); } const POPOVER_PROPS = { constrainSize: !0, placement: "bottom", portal: !0 }, ZoomMenuView = react.memo(function(props) { var _a; const { disabled, setZoom, zoom } = props; return /* @__PURE__ */ jsxRuntime.jsx( ui.MenuButton, { button: /* @__PURE__ */ jsxRuntime.jsx( ui.Button, { disabled, fontSize: 1, iconRight: icons.SelectIcon, mode: "ghost", padding: 2, text: (_a = ZOOM_OPTIONS.find((o) => o.value === zoom)) == null ? void 0 : _a.title } ), id: "zoom-menu", menu: /* @__PURE__ */ jsxRuntime.jsx(ui.Menu, { children: ZOOM_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx( ui.MenuItem, { fontSize: 1, onClick: () => setZoom(option.value), padding: 2, selected: option.value === zoom, text: option.title }, option.value )) }), popover: POPOVER_PROPS } ); }), WorkshopNavbar = react.memo(function(props) { const { inspectorExpanded, navigatorExpanded, onInspectorToggle, onNavigatorToggle } = props, mediaIndex = ui.useMediaIndex(), { story, title } = useWorkshop(); return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { borderBottom: !0, flex: "none", padding: 2, style: { lineHeight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { gap: 1, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { display: ["block", "block", "none"], flex: "none", children: /* @__PURE__ */ jsxRuntime.jsx( ui.Button, { "aria-label": "Open navigator", fontSize: [2, 2, 1], icon: icons.MenuIcon, mode: "bleed", onClick: onNavigatorToggle, padding: 2, selected: navigatorExpanded } ) }), /* @__PURE__ */ jsxRuntime.jsxs( ui.Flex, { flex: 1, justify: ["center", "center", "flex-start"], padding: 2, sizing: "border", style: { minWidth: 250 }, children: [ mediaIndex < 2 && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "bold", children: (story == null ? void 0 : story.title) || title }), mediaIndex >= 2 && /* @__PURE__ */ jsxRuntime.jsx(NavbarBreadcrumbs, {}) ] } ), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { display: ["block", "block", "none"], flex: "none", children: /* @__PURE__ */ jsxRuntime.jsx( ui.Button, { "aria-label": "Open inspector", fontSize: [2, 2, 1], icon: icons.ControlsIcon, mode: "bleed", onClick: onInspectorToggle, padding: 2, selected: inspectorExpanded } ) }), /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { display: ["none", "none", "block"], flex: "none", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Inline, { space: 1, children: [ /* @__PURE__ */ jsxRuntime.jsx(OpenCanvasButton, {}), /* @__PURE__ */ jsxRuntime.jsx(ViewportMenu, {}), /* @__PURE__ */ jsxRuntime.jsx(ZoomMenu, {}), /* @__PURE__ */ jsxRuntime.jsx(SchemeMenu, {}) ] }) }) ] }) }); }); function parseMenuNode(collections, node, name) { if (node.__scope__) { const scope = node.__scope__; return scope.name === "@@root@@" ? scope.stories.map((s) => ({ type: "story", ...s })) : [ { type: "scope", name: scope.name || "@@root@@", title: scope.title || "(root)", scope } ]; } const coll = collections.find((c) => c.name === name), items = Object.entries(node).filter(([key]) => key !== "__scope__").flatMap( ([key, child]) => parseMenuNode((coll == null ? void 0 : coll.children) || [], child, key) ); return [ { type: "list", name, title: (coll == null ? void 0 : coll.title) || name, items } ]; } function buildMenu(collections, scopes) { const scopeMap = {}; for (const scope of scopes) { const scopeName = scope.name || "@@root@@", prevScope = scopeMap[scopeName] || { name: scopeName, title: scope.title, stories: [] }, mergedScope = { ...prevScope, name: scopeName, stories: prevScope.stories.concat(scope.stories) }; scopeMap[scopeName] = mergedScope; } let tree = {}; for (const scope of Object.values(scopeMap)) tree = segmentedProperty.set(tree, scope.name || "@@root@@", { __scope__: scope }); const rootNode = { type: "list", name: "@@root@@", items: [] }; for (const [key, entry] of Object.entries(tree)) rootNode.items.push(...parseMenuNode(collections, entry, key)); return rootNode; } const SearchResults = react.memo(function(props) { const { matches, onStoryClick } = props; return /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 1, children: matches.map(({ scope, story }) => /* @__PURE__ */ jsxRuntime.jsx( ui.Card, { as: "a", "data-path": `/${scope.name}/${story.name}`, href: `/${scope.name}/${story.name}`, onClick: onStoryClick, padding: 2, radius: 2, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, textOverflow: "ellipsis", children: [scope.title || "", story.title].filter(Boolean).join(" / ") }) }, `${scope.name}/${story.name}` )) }); }), StoryTree = react.memo(function(props) { const { items } = props; return /* @__PURE__ */ jsxRuntime.jsx(ui.Tree, { space: 1, children: /* @__PURE__ */ jsxRuntime.jsx(MenuItems, { items }) }); }), MenuItems = react.memo(function(props) { const { basePath = "", items } = props, { broadcast, path: workshopPath, scope: currentScope, story: currentStory } = useWorkshop(), handleStoryClick = react.useCallback( (event) => { event.preventDefault(); const targetPath = event.currentTarget.getAttribute("data-path"); targetPath && broadcast({ type: "workshop/setPath", value: targetPath }); }, [broadcast] ); return react.useMemo(() => items.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: items.map((item, itemIndex) => { if (item.type === "list") { const path = `${basePath}/${item.name}`; return /* @__PURE__ */ jsxRuntime.jsx( MemoList, { expanded: workshopPath.startsWith(path + "/"), item, path }, item.name || itemIndex ); } return item.type === "story" ? /* @__PURE__ */ jsxRuntime.jsx( ui.TreeItem, { "data-path": `/${item.name || ""}`, fontSize: [2, 2, 1], href: `/${item.name || ""}`, onClick: handleStoryClick, padding: 2, selected: (currentStory == null ? void 0 : currentStory.component) === item.component, text: item.title }, item.name ) : item.type === "scope" ? /* @__PURE__ */ jsxRuntime.jsx( MemoScope, { currentStory, expanded: item.scope === currentScope, item, onStoryClick: handleStoryClick }, item.name ) : /* @__PURE__ */ jsxRuntime.jsx(ui.TreeItem, { text: "unknown" }, itemIndex); }) }), [basePath, currentScope, currentStory, handleStoryClick, items, workshopPath]); }), MemoList = react.memo(function(props) { const { expanded, item, path } = props, children = react.useMemo(() => /* @__PURE__ */ jsxRuntime.jsx(MenuItems, { basePath: path, items: item.items }), [item, path]); return /* @__PURE__ */ jsxRuntime.jsx( ui.TreeItem, { expanded, fontSize: [2, 2, 1], padding: 2, text: item.title, weight: "semibold", children } ); }), MemoScope = react.memo(function(props) { const { currentStory, expanded, item, onStoryClick } = props, children = react.useMemo( () => item.scope.stories.map((story) => /* @__PURE__ */ jsxRuntime.jsx( ui.TreeItem, { "data-path": `/${item.scope.name}/${story.name}`, fontSize: [2, 2, 1], href: `/${item.scope.name}/${story.name}`, onClick: onStoryClick, padding: 2, selected: currentStory === story, text: story.title }, story.name )), [currentStory, item, onStoryClick] ); return /* @__PURE__ */ jsxRuntime.jsx( ui.TreeItem, { expanded, fontSize: [2, 2, 1], padding: 2, text: item.title, weight: "semibold", children } ); }), Root$2 = styled__default.default(ui.Card)` overflow: hidden; @media screen and (min-width: ${({ theme }) => theme.sanity.media[1]}px) { border-right: 1px solid var(--card-border-color); min-width: 180px; max-width: 300px; overflow: auto; } `, flexNoneStyle = { flex: "none" }, lineHeightNoneStyle = { lineHeight: 0 }, textInputFontSize = [2, 2, 1], WorkshopNavigator = react.memo(function(props) { const { collections = [], expanded } = props, { broadcast, scopes } = useWorkshop(), menu = react.useMemo(() => buildMenu(collections, scopes), [collections, scopes]), [query, setQuery] = react.useState(""), matches = react.useMemo(() => { var _a; if (!query) return EMPTY_ARRAY; const q = query.toLowerCase(), ret = []; for (const scope of scopes) for (const story of scope.stories) ((_a = scope.title) != null && _a.toLowerCase().includes(q) || story.title.toLowerCase().includes(q)) && ret.push({ scope, story }); return ret; }, [query, scopes]), handleSearchQueryChange = react.useCallback( (event) => setQuery(event.currentTarget.value), [] ), handleSearchQueryClear = react.useCallback(() => setQuery(""), []), handleStoryClick = react.useCallback( (event) => { event.preventDefault(); const targetPath = event.currentTarget.getAttribute("data-path"); targetPath && (broadcast({ type: "workshop/setPath", value: targetPath }), setQuery("")); }, [broadcast] ); return /* @__PURE__ */ jsxRuntime.jsx( NavigatorView, { expanded, matches, menu, onSearchQueryChange: handleSearchQueryChange, onSearchQueryClear: handleSearchQueryClear, onStoryClick: handleStoryClick, query } ); }), NavigatorView = react.memo(function(props) { const { expanded, matches, menu, onSearchQueryChange, onSearchQueryClear, onStoryClick, query } = props, display = react.useMemo( () => expanded ? ["block"] : ["none", "none", "block"], [expanded] ); return /* @__PURE__ */ jsxRuntime.jsx(Root$2, { display, flex: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", height: "fill", children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Layer, { style: flexNoneStyle, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 2, shadow: 1, style: lineHeightNoneStyle, children: /* @__PURE__ */ jsxRuntime.jsx( ui.TextInput, { border: !1, clearButton: !!query, fontSize: textInputFontSize, icon: icons.SearchIcon, onChange: onSearchQueryChange, onClear: onSearchQueryClear, padding: 2, placeholder: "Stories", radius: 2, space: 2, value: query } ) }) }), /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, { flex: 1, overflow: "auto", children: [ query && matches.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(SearchResults, { matches, onStoryClick }) }), !query && menu.type === "list" && /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 2, children: /* @__PURE__ */ jsxRuntime.jsx(StoryTree, { items: menu.items }) }) ] }) ] }) }); }), Frame = styled__default.default.iframe` display: block; border: 0; height: 100%; width: 100%; `, WorkshopCanvas = react.memo(function(props) { const { frameRef, hidden } = props, { frameReady, frameUrl, path, payload, scheme, title, viewport, zoom } = useWorkshop(), viewportOption = VIEWPORT_OPTIONS.find((o) => o.name === viewport) || VIEWPORT_OPTIONS[0], viewportW = viewportOption == null ? void 0 : viewportOption.rect.width, viewportH = viewportOption == null ? void 0 : viewportOption.rect.height, [initialFrameUrl] = react.useState( () => buildFrameUrl({ baseUrl: frameUrl, path, payload, scheme, viewport, zoom }) ), containerStyle = react.useMemo( () => ({ maxWidth: viewportW === "auto" ? void 0 : `${(viewportW || 1) * zoom}px`, maxHeight: viewportH ? `${(viewportH || 1) * zoom}px` : void 0 }), [viewportW, viewportH, zoom] ), display = react.useMemo(() => hidden ? "none" : "block", [hidden]), frameStyle = react.useMemo( () => ({ transform: `scale(${zoom})`, transformOrigin: "0 0", width: `${100 / zoom}%`, height: `${100 / zoom}%` }), [zoom] ); return /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { display, flex: 1, overflow: "hidden", tone: "transparent", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", height: "fill", justify: "center", sizing: "border", children: [ path === "/" && /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { width: 0, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { padding: 4, space: 4, children: [ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { align: "center", children: title }), /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { align: "center", muted: !0, children: "Browse workshop stories in the navigator to the left." }) ] }) }), !frameReady && path !== "/" && /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, { muted: !0 }), /* @__PURE__ */ jsxRuntime.jsx( ui.Container, { height: "fill", hidden: !frameReady || path === "/", style: containerStyle, width: "auto", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { height: "fill", shadow: 1, children: /* @__PURE__ */ jsxRuntime.jsx(Frame, { ref: frameRef, src: initialFrameUrl, style: frameStyle }) }) } ) ] }) }); }); function createWorkshopFrameController() { const _subscribers = /* @__PURE__ */ new Set(); let _frameElement = null, _msgQueue = [], _flushTimeout = null; function _flush() { _flushTimeout && clearInterval(_flushTimeout), _flushTimeout = setTimeout(() => { var _a; (_a = _frameElement == null ? void 0 : _frameElement.contentWindow) == null || _a.postMessage(_msgQueue, window.location.origin), _msgQueue = [], _flushTimeout = null; }, 0); } function _handleMessage(event) { const msgs = event.data; if (isArray(msgs)) { for (const msg of msgs) if (isRecord(msg) && typeof msg.type == "string" && msg.type.startsWith("workshop/")) for (const subscriber of _subscribers) subscriber(msg); } } function _mount() { _frameElement != null && _frameElement.contentWindow && window.addEventListener("message", _handleMessage); } function _unmount(el) { el != null && el.contentWindow && window.removeEventListener("message", _handleMessage); } return { message: { publish(msg) { _msgQueue.push(msg), _flush(); }, subscribe(subscriber) { return _subscribers.add(subscriber), () => { _subscribers.delete(subscriber); }; } }, setElement(el) { const prevFrameElement = _frameElement; _frameElement = el, el ? _mount() : prevFrameElement && _unmount(prevFrameElement); } }; } function getStateFromLocation(loc, schemeProp, frameReady) { const path = loc.path, query = loc.query || {}, { scheme, viewport, zoom, ...payload } = query; return { frameReady: frameReady || !1, path, payload, scheme: schemeProp || (typeof scheme == "string" ? scheme : "light"), viewport: typeof viewport == "string" ? viewport : "auto", zoom: typeof zoom == "number" ? zoom : 1 }; } function getQueryFromState(state, withPayload = !0) { const { payload, scheme, viewport, zoom } = state, query = { scheme }; if (viewport && viewport !== DEFAULT_VIEWPORT_VALUE && (query.viewport = viewport), zoom && zoom !== DEFAULT_ZOOM_VALUE && (query.zoom = zoom), withPayload) for (const [key, val] of Object.entries(payload)) ["schema", "viewport", "zoom"].includes(key) ? console.warn( `Workshop: the payload cannot contain a property named "${key}" (protected name)` ) : query[key] = val; return query; } const Workshop = react.memo(function(props) { var _a, _b; const { config, locationStore, onSchemeChange, scheme: schemeProp } = props, withNavbar = (_b = (_a = config.features) == null ? void 0 : _a.navbar) != null ? _b : !0, channel = react.useMemo(() => createPubsub(), []), frame = react.useMemo(() => createWorkshopFrameController(), []), [boundaryElement, setBoundaryElement] = react.useState(null), [portalElement, setPortalElement] = react.useState(null), [{ frameReady, path, payload, scheme, viewport, zoom }, setState] = react.useState( () => getStateFromLocation(locationStore.get(), schemeProp) ), mediaIndex = ui.useMediaIndex(), [navigatorExpanded, setNavigatorExpanded] = react.useState(!1), [inspectorExpanded, setInspectorExpanded] = react.useState(!1), frameReadyRef = react.useRef(frameReady), schemeRef = react.useRef(scheme), pathRef = react.useRef(path), queryRef = react.useRef({ scheme, viewport, zoom, ...payload }), broadcast = react.useCallback( (msg) => { channel.publish(msg), frame.message.publish(msg); }, [channel, frame] ), _pushLocation = react.useMemo( () => debounce((loc) => locationStore.push(loc), 150), [locationStore] ), _replaceLocation = react.useMemo( () => debounce((loc) => locationStore.replace(loc), 150), [locationStore] ), handleNavigatorToggle = react.useCallback(() => { setNavigatorExpanded((v) => !v), setInspectorExpanded(!1); }, []), handleInspectorToggle = react.useCallback(() => { setNavigatorExpanded(!1), setInspectorExpanded((v) => !v); }, []), mediaIndexRef = react.useRef(mediaIndex); return react.useEffect(() => { mediaIndexRef.current < 2 && mediaIndex >= 2 && (setNavigatorExpanded(!1), setInspectorExpanded(!1)), mediaIndexRef.current = mediaIndex; }, [mediaIndex]), react.useEffect(() => { setNavigatorExpanded(!1); }, [path]), react.useEffect(() => () => _pushLocation.cancel(), [_pushLocation]), react.useEffect(() => () => _replaceLocation.cancel(), [_replaceLocation]), react.useEffect( () => channel.subscribe((msg) => { setState((prevState) => { const nextState = workshopReducer(prevState, msg), changed = !isEqual(prevState, nextState); if (changed) { if (msg.type === "workshop/setPath") { if (pathRef.current !== nextState.path) { pathRef.current = nextState.path; const nextQuery = getQueryFromState(nextState, !1); _pushLocation({ path: nextState.path, query: nextQuery }); } } else if (msg.type !== "workshop/setState") { const nextQuery = getQueryFromState(nextState); isEqual(queryRef.current, nextQuery) || (queryRef.current = nextQuery, _replaceLocation({ path: nextState.path, query: nextQuery })); } } return changed ? nextState : prevState; }); }), [_pushLocation, _replaceLocation, channel, locationStore] ), react.useEffect(() => frame.message.subscribe(channel.publish), [channel, frame]), react.useEffect(() => { frameReadyRef.current = frameReady; }, [frameReady]), react.useEffect( () => locationStore.subscribe((loc) => { const nextState = getStateFromLocation(loc, void 0, frameReady); broadcast({ type: "workshop/setState", value: nextState }); }), [broadcast, frameReady, locationStore] ), react.useEffect(() => { schemeRef.current = scheme, onSchemeChange(scheme); }, [onSchemeChange, scheme]), react.useEffect(() => { schemeProp && schemeRef.current !== schemeProp && (schemeRef.current = schemeProp, broadcast({ type: "workshop/setScheme", value: schemeProp })); }, [broadcast, schemeProp]), config.scopes ? /* @__PURE__ */ jsxRuntime.jsx( WorkshopProvider, { config, broadcast, channel, frameReady, origin: "main", path, payload, scheme, viewport, zoom, children: /* @__PURE__ */ jsxRuntime.jsx(ui.ToastProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.BoundaryElementProvider, { element: boundaryElement, children: /* @__PURE__ */ jsxRuntime.jsx(ui.PortalProvider, { element: portalElement, children: /* @__PURE__ */ jsxRuntime.jsxs( ui.Flex, { "data-boundary": "", direction: "column", height: "fill", ref: setBoundaryElement, style: { minWidth: 320 }, children: [ withNavbar && /* @__PURE__ */ jsxRuntime.jsx( WorkshopNavbar, { inspectorExpanded, navigatorExpanded, onInspectorToggle: handleInspectorToggle, onNavigatorToggle: handleNavigatorToggle } ), /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { flex: 1, children: [ /* @__PURE__ */ jsxRuntime.jsx(WorkshopNavigator, { collections: config.collections, expanded: navigatorExpanded }), /* @__PURE__ */ jsxRuntime.jsx( WorkshopCanvas, { frameRef: frame.setElement, hidden: navigatorExpanded || inspectorExpanded } ), /* @__PURE__ */ jsxRuntime.jsx(WorkshopInspector, { expanded: inspectorExpanded }) ] }), /* @__PURE__ */ jsxRuntime.jsx("div", { "data-portal": "", ref: setPortalElement }) ] } ) }) }) }) } ) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: "No scopes" }); }); function mount(options) { const { config, element } = options; if (!element) throw new Error("missing element"); client.createRoot(element).render( /* @__PURE__ */ jsxRuntime.jsx(react.StrictMode, { children: /* @__PURE__ */ jsxRuntime.jsx(Root$1, { config }) }) ); } function Root$1(props) { const { config } = props, prefersDark = ui.usePrefersDark(), [scheme, setScheme] = react.useState(prefersDark ? "dark" : "light"), locationStore = react.useMemo(() => createLocationStore(), []); return react.useEffect(() => { setScheme(prefersDark ? "dark" : "light"); }, [prefersDark]), /* @__PURE__ */ jsxRuntime.jsxs(ui.ThemeProvider, { scheme, theme: config.theme || ui.studioTheme, children: [ /* @__PURE__ */ jsxRuntime.jsx(GlobalStyle, {}), /* @__PURE__ */ jsxRuntime.jsx( Workshop, { config, locationStore, scheme, onSchemeChange: setScheme } ) ] }); } function mountFrame(options) { const { config, element } = options; if (!element) throw new Error("missing element"); client.createRoot(element).render( /* @__PURE__ */ jsxRuntime.jsx(react.StrictMode, { children: /* @__PURE__ */ jsxRuntime.jsx(Root, { config }) }) ); } function Root(props) { const { config } = props, prefersDark = ui.usePrefersDark(), [scheme, setScheme] = react.useState(prefersDark ? "dark" : "light"); return /* @__PURE__ */ jsxRuntime.jsxs(ui.ThemeProvider, { scheme, theme: config.theme || ui.studioTheme, children: [ /* @__PURE__ */ jsxRuntime.jsx(GlobalStyle, {}), /* @__PURE__ */ jsxRuntime.jsx(WorkshopFrame, { config, setScheme }) ] }); } function useAction(name, options) { const { preventDefault = !1 } = options || {}, { scope, story } = useWorkshop(); return react.useCallback( (...args) => { if (!scope || !story) return; const ev = args[0]; preventDefault && "preventDefault" in ev && typeof ev.preventDefault == "function" && ev.preventDefault(), console.log(`[${scope.name}/${story.name}]`, name, ...args); }, [preventDefault, scope, story, name] ); } exports.DEFAULT_VIEWPORT_VALUE = DEFAULT_VIEWPORT_VALUE; exports.DEFAULT_ZOOM_VALUE = DEFAULT_ZOOM_VALUE; exports.EMPTY_ARRAY = EMPTY_ARRAY; exports.EMPTY_RECORD = EMPTY_RECORD; exports.VIEWPORT_OPTIONS = VIEWPORT_OPTIONS; exports.Workshop = Workshop; exports.WorkshopFrame = WorkshopFrame; exports.WorkshopProvider = WorkshopProvider; exports.ZOOM_OPTIONS = ZOOM_OPTIONS; exports.createLocationStore = createLocationStore; exports.createPubsub = createPubsub; exports.defineConfig = defineConfig; exports.defineRuntime = defineRuntime; exports.defineScope = defineScope; exports.mount = mount; exports.mountFrame = mountFrame; exports.propsPlugin = propsPlugin; exports.useAction = useAction; exports.useBoolean = useBoolean; exports.useNumber = useNumber; exports.useProps = useProps; exports.useSelect = useSelect; exports.useString = useString; exports.useText = useText; exports.useWorkshop = useWorkshop; exports.workshopReducer = workshopReducer; //# sourceMappingURL=index.cjs.map