UNPKG

@remotion/studio

Version:

APIs for interacting with the Remotion Studio

312 lines (311 loc) 12.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.QuickSwitcherContent = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const remotion_1 = require("remotion"); const colors_1 = require("../../helpers/colors"); const is_composition_still_1 = require("../../helpers/is-composition-still"); const use_keybinding_1 = require("../../helpers/use-keybinding"); const use_menu_structure_1 = require("../../helpers/use-menu-structure"); const modals_1 = require("../../state/modals"); const InitialCompositionLoader_1 = require("../InitialCompositionLoader"); const KeyboardShortcutsExplainer_1 = require("../KeyboardShortcutsExplainer"); const layout_1 = require("../layout"); const is_menu_item_1 = require("../Menu/is-menu-item"); const RemInput_1 = require("../NewComposition/RemInput"); const algolia_search_1 = require("./algolia-search"); const AlgoliaCredit_1 = require("./AlgoliaCredit"); const fuzzy_search_1 = require("./fuzzy-search"); const NoResults_1 = require("./NoResults"); const QuickSwitcherResult_1 = require("./QuickSwitcherResult"); const input = { width: '100%', }; const modeSelector = { paddingLeft: 16, paddingRight: 16, display: 'flex', flexDirection: 'row', paddingTop: 8, paddingBottom: 5, }; const modeItem = { appearance: 'none', border: 'none', fontFamily: 'inherit', padding: 0, fontSize: 13, cursor: 'pointer', }; const modeInactive = { ...modeItem, color: colors_1.LIGHT_TEXT, }; const modeActive = { ...modeItem, color: 'white', fontWeight: 'bold', }; const content = { paddingLeft: 16, paddingRight: 16, paddingTop: 4, paddingBottom: 10, display: 'flex', flexDirection: 'row', alignItems: 'center', }; const loopIndex = (index, length) => { if (index < 0) { index += length; } return index % length; }; const stripQuery = (query) => { if (query.startsWith('>') || query.startsWith('?')) { return query.substring(1).trim(); } return query.trim(); }; const mapQueryToMode = (query) => { return query.startsWith('>') ? 'commands' : query.startsWith('?') ? 'docs' : 'compositions'; }; const mapModeToQuery = (mode) => { if (mode === 'commands') { return '> '; } if (mode === 'compositions') { return ''; } if (mode === 'docs') { return '? '; } throw new Error('no mode' + mode); }; const QuickSwitcherContent = ({ initialMode, invocationTimestamp, readOnlyStudio }) => { const { compositions } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager); const [state, setState] = (0, react_1.useState)(() => { return { query: mapModeToQuery(initialMode), selectedIndex: 0, }; }); (0, react_1.useEffect)(() => { setState({ query: mapModeToQuery(initialMode), selectedIndex: 0, }); }, [initialMode, invocationTimestamp]); const inputRef = (0, react_1.useRef)(null); const selectComposition = (0, InitialCompositionLoader_1.useSelectComposition)(); const closeMenu = (0, react_1.useCallback)(() => undefined, []); const actions = (0, use_menu_structure_1.useMenuStructure)(closeMenu, readOnlyStudio); const [docResults, setDocResults] = (0, react_1.useState)({ type: 'initial' }); const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext); const keybindings = (0, use_keybinding_1.useKeybinding)(); const mode = mapQueryToMode(state.query); const actualQuery = (0, react_1.useMemo)(() => { return stripQuery(state.query); }, [state.query]); const menuActions = (0, react_1.useMemo)(() => { if (mode !== 'commands') { return []; } return (0, use_menu_structure_1.makeSearchResults)(actions, setSelectedModal); }, [actions, mode, setSelectedModal]); const resultsArray = (0, react_1.useMemo)(() => { if (mode === 'commands') { return (0, fuzzy_search_1.fuzzySearch)(actualQuery, menuActions); } if (mode === 'docs' && docResults.type === 'results') { return docResults.results; } return (0, fuzzy_search_1.fuzzySearch)(actualQuery, compositions.map((c) => { return { id: 'composition-' + c.id, title: c.id, type: 'composition', onSelected: () => { var _a; selectComposition(c, true); setSelectedModal(null); const selector = `.__remotion-composition[data-compname="${c.id}"]`; (_a = remotion_1.Internals.compositionSelectorRef.current) === null || _a === void 0 ? void 0 : _a.expandComposition(c.id); waitForElm(selector).then(() => { var _a; (_a = document .querySelector(selector)) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'center' }); }); }, compositionType: (0, is_composition_still_1.isCompositionStill)(c) ? 'still' : 'composition', }; })); }, [ mode, actualQuery, compositions, menuActions, docResults, selectComposition, setSelectedModal, ]); const onArrowDown = (0, react_1.useCallback)(() => { setState((s) => { return { ...s, selectedIndex: s.selectedIndex + 1, }; }); }, []); const onArrowUp = (0, react_1.useCallback)(() => { setState((s) => { return { ...s, selectedIndex: s.selectedIndex - 1, }; }); }, []); // Arrow up (0, react_1.useEffect)(() => { const binding = keybindings.registerKeybinding({ key: 'ArrowUp', callback: onArrowUp, commandCtrlKey: false, event: 'keydown', preventDefault: true, // Will be using the input field while selecting triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false, }); return () => { binding.unregister(); }; }, [keybindings, onArrowDown, onArrowUp]); (0, react_1.useEffect)(() => { if (mode !== 'docs') { return; } if (actualQuery.trim() === '') { setDocResults({ type: 'initial' }); return; } let cancelled = false; setDocResults({ type: 'loading' }); (0, algolia_search_1.algoliaSearch)(actualQuery) .then((agoliaResults) => { if (cancelled) { return; } setDocResults({ type: 'results', results: agoliaResults }); }) .catch((err) => { if (cancelled) { return; } setDocResults({ type: 'error', error: err }); }); return () => { cancelled = true; }; }, [actualQuery, mode]); // Arrow down (0, react_1.useEffect)(() => { const binding = keybindings.registerKeybinding({ key: 'ArrowDown', callback: onArrowDown, commandCtrlKey: false, event: 'keydown', preventDefault: true, // Will be using the input field while selecting triggerIfInputFieldFocused: true, keepRegisteredWhenNotHighestContext: false, }); return () => { binding.unregister(); }; }, [keybindings, onArrowDown]); const onTextChange = (0, react_1.useCallback)((e) => { setState({ query: e.target.value, selectedIndex: 0 }); }, []); const selectedIndexRounded = loopIndex(state.selectedIndex, resultsArray.length); const onActionsSelected = (0, react_1.useCallback)(() => { var _a; setState((s) => ({ query: `> ${stripQuery(s.query)}`, selectedIndex: 0, })); (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []); const onCompositionsSelected = (0, react_1.useCallback)(() => { var _a; setState((s) => ({ query: stripQuery(s.query), selectedIndex: 0, })); (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []); const onDocSearchSelected = (0, react_1.useCallback)(() => { var _a; setState((s) => ({ query: `? ${stripQuery(s.query)}`, selectedIndex: 0, })); setDocResults({ type: 'initial' }); (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []); const showKeyboardShortcuts = mode === 'docs' && actualQuery.trim() === ''; const showSearchLoadingState = mode === 'docs' && docResults.type === 'loading'; const container = (0, react_1.useMemo)(() => { return { width: showKeyboardShortcuts ? 800 : 500, }; }, [showKeyboardShortcuts]); const results = (0, react_1.useMemo)(() => { if (showKeyboardShortcuts) { return { maxHeight: 600, overflowY: 'auto', }; } return { overflowY: 'auto', height: 300, }; }, [showKeyboardShortcuts]); return (jsx_runtime_1.jsxs("div", { style: container, children: [ jsx_runtime_1.jsxs("div", { style: modeSelector, children: [ jsx_runtime_1.jsx("button", { onClick: onCompositionsSelected, style: mode === 'compositions' ? modeActive : modeInactive, type: "button", children: "Compositions" }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("button", { onClick: onActionsSelected, style: mode === 'commands' ? modeActive : modeInactive, type: "button", children: "Actions" }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 1 }), jsx_runtime_1.jsx("button", { onClick: onDocSearchSelected, style: mode === 'docs' ? modeActive : modeInactive, type: "button", children: "Documentation" }) ] }), jsx_runtime_1.jsxs("div", { style: content, children: [ jsx_runtime_1.jsx(RemInput_1.RemotionInput, { ref: inputRef, type: "text", style: input, autoFocus: true, status: "ok", value: state.query, onChange: onTextChange, placeholder: "Search compositions...", rightAlign: false }), showKeyboardShortcuts ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [ jsx_runtime_1.jsx(layout_1.Spacing, { x: 2 }), " ", jsx_runtime_1.jsx(AlgoliaCredit_1.AlgoliaCredit, {}) ] })) : null] }), jsx_runtime_1.jsx("div", { style: results, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: showKeyboardShortcuts ? (jsx_runtime_1.jsx(KeyboardShortcutsExplainer_1.KeyboardShortcutsExplainer, {})) : showSearchLoadingState ? null : resultsArray.length === 0 ? (jsx_runtime_1.jsx(NoResults_1.QuickSwitcherNoResults, { mode: mode, query: actualQuery })) : (resultsArray.map((result, i) => { return (jsx_runtime_1.jsx(QuickSwitcherResult_1.QuickSwitcherResult, { selected: selectedIndexRounded === i, result: result }, result.id)); })) }) ] })); }; exports.QuickSwitcherContent = QuickSwitcherContent; function waitForElm(selector) { return new Promise((resolve) => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); return; } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { resolve(document.querySelector(selector)); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true, }); }); }