"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
exports.__esModule = true;
exports.ToggleColorModeButton = exports.Couple = exports.ExperimentPage = void 0;
var react_1 = require("@chakra-ui/react");
var jotai_1 = require("jotai");
var react_2 = __importStar(require("react"));
var fa_1 = require("react-icons/fa");
var fi_1 = require("react-icons/fi");
var md_1 = require("react-icons/md");
var react_is_1 = require("react-is");
var history_1 = require("../history");
var support_1 = require("../support");
var ClickToSource_1 = require("./ClickToSource");
var DefaultWrapper_1 = require("./DefaultWrapper");
var MarkdownStyler_1 = require("./MarkdownStyler");
var MobileNav_1 = require("./MobileNav");
var atomCssDebug = jotai_1.atom(false);
var atomClickToSource = jotai_1.atom('');
var atomStoryInFullScreen = jotai_1.atom(history_1.getParam(window.location.href, 'inFullScreen') || '');
var mdxComponentPrefix = 'VitroMdx';
var exportsOrderingName = '__vitroExportsOrdering';
function ExperimentPage(_a) {
    var _b, _c;
    var experimentsTree = _a.experimentsTree, sourceExperimentPath = _a.sourceExperimentPath, config = _a.config, root = _a.root, componentsOverrides = _a.componentsOverrides, _d = _a.fileExports, getFileExports = _d === void 0 ? function () { return Promise.resolve({}); } : _d;
    var _e = react_1.useColorMode(), colorMode = _e.colorMode, toggleColorMode = _e.toggleColorMode;
    var _f = jotai_1.useAtom(atomCssDebug), setCssDebug = _f[1];
    var preferredClickToSourceProvider = getPreferredProvider(config.links); // TODO make preferredClickToSourceProvider configurable
    var _g = jotai_1.useAtom(atomClickToSource), clickToSeeSourceProvider = _g[0], setClickToSeeSource = _g[1];
    var _h = jotai_1.useAtom(atomStoryInFullScreen), setStoryInFullScreen = _h[1];
    history_1.useRouteChanged(function () {
        var newStory = history_1.getParam(window.location.href, 'inFullScreen');
        setStoryInFullScreen(newStory); // TODO this will be called on every url change
    });
    var importResult = support_1.usePromise(getFileExports, {});
    // TODO make useComponentExport hook that memoizes a component export and check it's valid component
    // exported.then(z => console.log(Object.keys(z)))
    // console.log({ fileExports })
    var experimentTitle = importResult.loading
        ? ''
        : ((_c = (_b = importResult.value) === null || _b === void 0 ? void 0 : _b["default"]) === null || _c === void 0 ? void 0 : _c.title) ||
            support_1.formatPathToTitle(sourceExperimentPath) ||
            '';
    var bg = react_2.useMemo(function () { return ({ light: 'white', dark: 'gray.700' }[colorMode]); }, [colorMode]);
    // if (!exported || !story || !storyObject) {
    //     // TODO return 404
    //     return null
    // }
    return (react_2["default"].createElement(react_1.Stack, { spacing: '10' },
        react_2["default"].createElement(react_1.Stack, { flexShrink: 0, align: 'flex-start', spacing: '4' },
            react_2["default"].createElement(react_1.Stack, { height: support_1.TOP_TITLE_H, spacing: '3', align: 'flex-end', direction: 'row', width: '100%' },
                react_2["default"].createElement(react_1.Box, { fontSize: '32px', minWidth: '0', width: 'auto', isTruncated: true, fontWeight: 'medium' }, importResult.loading ? (react_2["default"].createElement(react_1.Skeleton, null, "Loading the page")) : (experimentTitle)),
                react_2["default"].createElement(react_1.Box, { flex: '1' }),
                react_2["default"].createElement(exports.ToggleColorModeButton, { mr: ['0px', null, null, '-10px !important'] }),
                react_2["default"].createElement(MobileNav_1.MobileNav, { display: ['flex', null, null, 'none'], experimentsTree: experimentsTree })),
            react_2["default"].createElement(OpenInEditorLink, { filePath: sourceExperimentPath, root: root, linksConfig: config.links || {} })),
        react_2["default"].createElement(react_1.Stack, { align: 'center', flexShrink: 0, direction: 'row' },
            react_2["default"].createElement(react_1.Box, { flex: '1' }),
            react_2["default"].createElement(react_1.Button, { onClick: function () { return setCssDebug(function (x) { return !x; }); }, opacity: 0.8, bg: bg, w: 'auto' },
                react_2["default"].createElement(react_1.Box, { mr: '2', d: 'inline-block', as: fa_1.FaBug }),
                "CSS debug"),
            react_2["default"].createElement(react_1.Button, { bg: bg, 
                // placeholder='click to see source'
                onClick: function () {
                    return clickToSeeSourceProvider
                        ? setClickToSeeSource('')
                        : setClickToSeeSource(preferredClickToSourceProvider);
                }, w: 'auto', fontWeight: 500, as: react_1.Button },
                react_2["default"].createElement(react_1.Stack, { direction: 'row', spacing: '2' },
                    react_2["default"].createElement(react_1.Switch, { pointerEvents: 'none', "aria-readonly": true, onChange: function () { }, isChecked: Boolean(clickToSeeSourceProvider) }),
                    react_2["default"].createElement(react_1.Box, { minWidth: '190px' }, clickToSeeSourceProvider
                        ? 'Click to source enabled'
                        : 'Click to source disabled ')))),
        react_2["default"].createElement(react_1.Stack, { flexShrink: 0, 
            // direction='row'
            flexWrap: 'wrap', overflow: 'visible' },
            react_2["default"].createElement(MainContent, { config: config, root: root, componentsOverrides: componentsOverrides, sourceExperimentPath: sourceExperimentPath, importResult: importResult }))));
}
exports.ExperimentPage = ExperimentPage;
function getPreferredProvider(linksConfig) {
    if (linksConfig === void 0) { linksConfig = {}; }
    if (process.env.NODE_ENV === 'development') {
        return 'vscode';
    }
    if (linksConfig.github) {
        return 'github';
    }
    if (linksConfig.gitlab) {
        return 'gitlab';
    }
    console.warn('To enable click to source the vitro app owner needs to pass config.links option'); // TODO add doc links
    return '';
}
function MainContent(_a) {
    var importResult = _a.importResult, config = _a.config, root = _a.root, componentsOverrides = _a.componentsOverrides, sourceExperimentPath = _a.sourceExperimentPath;
    var colorMode = react_1.useColorMode().colorMode;
    var fileExportsObject = importResult.value;
    var storyInFullScreen = jotai_1.useAtom(atomStoryInFullScreen)[0];
    var _b = support_1.usePromise(componentsOverrides, {}), overridesExports = _b.value, overridesError = _b.error, overridesLoading = _b.loading;
    var loading = importResult.loading || overridesLoading;
    var error = importResult.error || overridesError;
    var ValidGlobalWrapper = react_2.useMemo(function () {
        return !overridesExports || !react_is_1.isValidElementType(overridesExports.Wrapper)
            ? DefaultWrapper_1.DefaultWrapper
            : overridesExports.Wrapper;
    }, [overridesExports, colorMode]);
    var ExperimentWrapper = react_2.useMemo(function () {
        var _a;
        var experimentWrapper = (_a = fileExportsObject === null || fileExportsObject === void 0 ? void 0 : fileExportsObject["default"]) === null || _a === void 0 ? void 0 : _a.wrapper;
        return experimentWrapper || FragmentLike;
    }, [fileExportsObject, colorMode]);
    var reorderedExportsKeys = react_2.useMemo(function () {
        var exportsOrder = (fileExportsObject === null || fileExportsObject === void 0 ? void 0 : fileExportsObject[exportsOrderingName]) || [];
        return Object.keys(fileExportsObject)
            .sort(function (a, b) {
            return exportsOrder.indexOf(a) - exportsOrder.indexOf(b);
        })
            .filter(function (k) {
            var Component = fileExportsObject[k];
            return (k !== 'default' &&
                k !== exportsOrderingName &&
                react_is_1.isValidElementType(Component));
        });
    }, [fileExportsObject]);
    if (error) {
        return (react_2["default"].createElement(react_1.Stack, null,
            react_2["default"].createElement(react_1.Box, { color: 'red.500', fontSize: '0.9em', wordBreak: 'break-word', whiteSpace: 'pre-wrap', as: 'pre' }, error === null || error === void 0 ? void 0 : error.stack)));
    }
    if (loading && storyInFullScreen) {
        return (react_2["default"].createElement(react_1.Skeleton, null,
            react_2["default"].createElement(react_1.Box, { bg: { dark: 'gray.800', light: 'gray.100' }[colorMode], zIndex: 100000, top: 0, opacity: 1, bottom: 0, right: 0, left: 0, position: 'fixed', borderRadius: '4px' })));
    }
    if (loading) {
        return (react_2["default"].createElement(react_1.Stack, { spacing: '16' }, new Array(3).fill(null).map(function (_, i) {
            return (react_2["default"].createElement(react_1.Skeleton, { borderRadius: '4px', key: i, width: '100%', height: '300px' }));
        })));
    }
    if (!loading && fileExportsObject) {
        if (storyInFullScreen) {
            var componentProps = {
                key: colorMode,
                isDark: colorMode == 'dark',
                isFullScreen: true
            };
            var Component = fileExportsObject[storyInFullScreen];
            if (!Component) {
                return null;
            }
            var id = sourceExperimentPath + "/" + storyInFullScreen;
            return (react_2["default"].createElement(StoryBlock, { root: root, config: config, exportName: storyInFullScreen, key: id, id: id },
                react_2["default"].createElement(ValidGlobalWrapper, __assign({}, componentProps),
                    react_2["default"].createElement(ExperimentWrapper, __assign({}, componentProps),
                        react_2["default"].createElement(Component, __assign({}, componentProps))))));
        }
        return (react_2["default"].createElement(react_1.Stack, { spacing: '16', maxWidth: '100%' }, reorderedExportsKeys.map(function (exportName) {
            // console.log({ Component })
            var Component = fileExportsObject[exportName];
            var id = sourceExperimentPath + "/" + exportName;
            if (exportName.startsWith(mdxComponentPrefix)) {
                return (react_2["default"].createElement(MarkdownStyler_1.MdxStyler, { key: id },
                    react_2["default"].createElement(Component, null)));
            }
            var componentProps = {
                key: colorMode + storyInFullScreen,
                isDark: colorMode == 'dark',
                isFullScreen: storyInFullScreen === exportName
            };
            return (react_2["default"].createElement(StoryBlock, { root: root, config: config, exportName: exportName, key: id, id: id },
                react_2["default"].createElement(ValidGlobalWrapper, __assign({}, componentProps),
                    react_2["default"].createElement(ExperimentWrapper, __assign({}, componentProps),
                        react_2["default"].createElement(Component, __assign({}, componentProps))))));
        })));
    }
    return null;
}
var StoryBlock = function (_a) {
    var children = _a.children, _b = _a.blockWidth, blockWidth = _b === void 0 ? '100%' : _b, id = _a.id, config = _a.config, root = _a.root, exportName = _a.exportName, rest = __rest(_a, ["children", "blockWidth", "id", "config", "root", "exportName"]);
    var fullScreenStory = jotai_1.useAtom(atomStoryInFullScreen)[0];
    var isFullScreen = exportName === fullScreenStory;
    var cssDebugEnabled = jotai_1.useAtom(atomCssDebug)[0];
    var _c = jotai_1.useAtom(atomClickToSource), clickToSeeSourceProvider = _c[0], setClickToSeeSource = _c[1];
    var colorMode = react_1.useColorMode().colorMode;
    var bg = react_2.useMemo(function () { return ({ light: 'white', dark: 'gray.700' }[colorMode]); }, [colorMode]);
    var fullScreenStyles = react_2.useMemo(function () { return ({
        w: '100vw',
        maxW: '100vw',
        minHeight: '100%',
        // p: '50px',
        pt: '20px',
        bg: bg,
        position: 'fixed',
        overflow: 'auto',
        top: '0',
        left: '0',
        right: '0',
        bottom: '0',
        zIndex: 100000
    }); }, [bg]);
    react_2.useEffect(function () {
        if (!react_2.Profiler) {
            console.warn('your current react version does not export `Profiler`, some features are disabled');
        }
    }, []);
    var actualDurationRef = react_2.useRef('0.00ms');
    var renderCount = react_2.useRef(0);
    // renderCount.current = 0
    // console.log({ id })
    var profile = react_2.useCallback(function (id, _, actualDuration) {
        // console.log({ id, actualDuration })
        renderCount.current++;
        actualDurationRef.current = actualDuration.toFixed(2) + 'ms';
    }, []);
    return (react_2["default"].createElement(react_1.Stack, __assign({ spacing: '3', flexShrink: 0, flexGrow: 0, minHeight: '340px', overflow: 'visible', flexBasis: blockWidth, position: 'relative', maxWidth: '100%', overflowX: 'auto' }, rest, (isFullScreen ? fullScreenStyles : {})),
        react_2["default"].createElement(react_1.Stack, { spacing: '8', mx: isFullScreen ? '20px' : '', direction: 'row', align: 'center' },
            react_2["default"].createElement(react_1.Box, { borderRadius: 'md', p: '4px', fontSize: '18px', fontWeight: 'medium' }, support_1.formatPathToTitle(exportName)),
            react_2["default"].createElement(react_1.Box, { flex: '1' }),
            react_2["default"].createElement(exports.Couple, { borderRadius: 'md', p: '4px', a: react_2["default"].createElement(react_1.Box, { as: fi_1.FiZap, size: '1em' }), b: react_2["default"].createElement(AutoUpdateBox, { fontWeight: '500', contentRef: actualDurationRef }) }),
            ")",
            react_2["default"].createElement(exports.Couple, { borderRadius: 'md', p: '4px', a: react_2["default"].createElement(react_1.Box, { as: fi_1.FiHash, size: '1em' }), b: react_2["default"].createElement(AutoUpdateBox, { fontWeight: '500', map: function (x) { return x + ' renders'; }, contentRef: renderCount }) }),
            ")",
            react_2["default"].createElement(react_1.IconButton, { borderRadius: 'md', 
                // isRound
                size: 'sm', onClick: function () {
                    return history_1.changeParam('inFullScreen', isFullScreen ? '' : exportName);
                }, fontSize: '1.4em', 
                // bg='transparent'
                icon: isFullScreen ? react_2["default"].createElement(md_1.MdFullscreenExit, null) : react_2["default"].createElement(md_1.MdFullscreen, null), "aria-label": 'full-screen' })),
        react_2["default"].createElement(react_1.Box
        // spacing='0'
        , { 
            // spacing='0'
            flexShrink: 0, flex: '1', minH: '340px', minHeight: '340px', "data-vitro-export-name": exportName, className: "" + support_1.VITRO_BLOCK_CLASS },
            react_2["default"].createElement(react_1.Stack, { minH: '340px', height: '100%', shadow: 'sm', flex: '1', borderRadius: '4px', overflow: 'hidden', overflowX: 'auto', bg: bg, spacing: '0', align: 'stretch', justify: 'center' },
                react_2["default"].createElement(ClickToSource_1.ClickToSourceProviderStateless, { root: root, value: {
                        provider: clickToSeeSourceProvider
                    }, linksConfig: config.links, onChange: function (state) {
                        setClickToSeeSource(state.provider);
                    } },
                    react_2["default"].createElement(react_1.Stack
                    // flex='1'
                    , { 
                        // flex='1'
                        width: '100%', height: '100%', minH: '100%', minW: '100%', spacing: '0', align: 'center', justify: 'center', className: "" + (cssDebugEnabled ? 'vitro-inspect-css' : '') },
                        react_2["default"].createElement(ErrorBoundary, null, react_2.Profiler ? (react_2["default"].createElement(react_2.Profiler, { id: id, onRender: profile }, children)) : (children))))))));
};
var AutoUpdateBox = function (_a) {
    var _b = _a.after, after = _b === void 0 ? 1000 : _b, map = _a.map, contentRef = _a.contentRef, rest = __rest(_a, ["after", "map", "contentRef"]);
    var _c = react_2.useState(null), _ = _c[0], render = _c[1];
    react_2.useEffect(function () {
        var id = setInterval(function () {
            render('');
        }, after);
        return function () { return clearInterval(id); };
    }, []);
    return (react_2["default"].createElement(react_1.Box, __assign({}, rest, { children: map ? map(contentRef.current) : contentRef.current })));
};
var ErrorBoundary = /** @class */ (function (_super) {
    __extends(ErrorBoundary, _super);
    function ErrorBoundary() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.state = { hasError: false, message: '', trace: '', traceOpen: false };
        return _this;
    }
    ErrorBoundary.getDerivedStateFromError = function (error, info) {
        // Update state so the next render will show the fallback UI.
        return {
            hasError: true,
            message: (error === null || error === void 0 ? void 0 : error.message) || 'no error message found',
            trace: error === null || error === void 0 ? void 0 : error.stack
        };
    };
    ErrorBoundary.prototype.componentDidCatch = function (error, errorInfo) {
        // You can also log the error to an error reporting service
        console.error(error, errorInfo);
    };
    ErrorBoundary.prototype.render = function () {
        var _this = this;
        var _a = this.state, hasError = _a.hasError, message = _a.message, trace = _a.trace, traceOpen = _a.traceOpen;
        if (hasError) {
            // You can render any custom fallback UI
            return (react_2["default"].createElement(react_1.Stack, { m: '20', fontWeight: '500', align: 'center', justify: 'center', color: 'red.500', spacing: '6', maxW: '100%' },
                react_2["default"].createElement(react_1.Stack, { align: 'center', spacing: '2' },
                    react_2["default"].createElement(react_1.Box, { fontWeight: 'bold', textAlign: 'center', display: 'inline-block' }, "Error"),
                    react_2["default"].createElement(react_1.Box, { textAlign: 'center', display: 'inline-block' }, message),
                    react_2["default"].createElement(react_1.Button, { display: 'inline-block', size: 'sm', variant: 'ghost', onClick: function () {
                            return _this.setState(function (x) { return (__assign(__assign({}, x), { traceOpen: !x.traceOpen })); });
                        } }, "see trace")),
                react_2["default"].createElement(react_1.Collapse, { style: { overflowX: 'auto', maxWidth: '100%' }, "in": traceOpen },
                    react_2["default"].createElement(react_1.Box, { fontSize: '0.9em', as: 'pre' }, trace))));
        }
        return this.props.children;
    };
    return ErrorBoundary;
}(react_2["default"].Component));
var Couple = function (_a) {
    var a = _a.a, b = _a.b, _b = _a.hide, hide = _b === void 0 ? false : _b, rest = __rest(_a, ["a", "b", "hide"]);
    return (react_2["default"].createElement(react_1.Stack
    // display={hide ? 'none' : ['none', null, 'flex']}
    , __assign({ 
        // display={hide ? 'none' : ['none', null, 'flex']}
        isTruncated: true, direction: 'row', align: 'center', spacing: '1' }, rest),
        react_2["default"].createElement(react_1.Box, null, a),
        react_2["default"].createElement(react_1.Box, null, b)));
};
exports.Couple = Couple;
var ToggleColorModeButton = function (_a) {
    var rest = __rest(_a, []);
    var _b = react_1.useColorMode(), colorMode = _b.colorMode, toggleColorMode = _b.toggleColorMode;
    return (react_2["default"].createElement(react_1.IconButton
    // bg={{ light: 'white', dark: 'gray.700' }[colorMode]}
    // size='sm'
    // shadow='sm'
    , __assign({ 
        // bg={{ light: 'white', dark: 'gray.700' }[colorMode]}
        // size='sm'
        // shadow='sm'
        onClick: toggleColorMode, fontSize: '1.2em', "aria-label": 'color-mode-toggle', icon: colorMode === 'dark' ? react_2["default"].createElement(fa_1.FaMoon, null) : react_2["default"].createElement(fi_1.FiSun, null) }, rest)));
};
exports.ToggleColorModeButton = ToggleColorModeButton;
function FragmentLike(_a) {
    var children = _a.children;
    return react_2["default"].createElement(react_2.Fragment, { children: children });
}
function OpenInEditorLink(_a) {
    var filePath = _a.filePath, root = _a.root, _b = _a.linksConfig, linksConfig = _b === void 0 ? {} : _b;
    var isDev = process.env.NODE_ENV == 'development';
    var href = '';
    var title = '';
    if (isDev) {
        href = "vscode://file" + filePath;
        title = 'Open in VScode';
    }
    else if (linksConfig.github) {
        href = ClickToSource_1.getGitCodeLink({
            linksConfig: linksConfig,
            filePath: filePath,
            root: root,
            provider: 'github'
        });
        title = 'Open in Github';
    }
    else if (linksConfig.gitlab) {
        href = ClickToSource_1.getGitCodeLink({
            linksConfig: linksConfig,
            filePath: filePath,
            root: root,
            provider: 'gitlab'
        });
        title = 'Open in Gitlab';
    }
    if (!href) {
        return null;
    }
    return (react_2["default"].createElement(react_1.Link, { target: '_blank', as: 'a', fontWeight: '500', href: href, opacity: 0.7 },
        react_2["default"].createElement(react_1.Box, { d: 'inline', size: '.8em', mr: '3', as: fa_1.FaLink }),
        title));
}
//# sourceMappingURL=ExperimentPage.js.map