@remotion/studio
Version:
APIs for interacting with the Remotion Studio
312 lines (311 loc) • 12.2 kB
JavaScript
"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,
});
});
}