@remotion/studio
Version:
APIs for interacting with the Remotion Studio
262 lines (261 loc) • 10.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MenuContent = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const colors_1 = require("../../helpers/colors");
const mobile_layout_1 = require("../../helpers/mobile-layout");
const use_keybinding_1 = require("../../helpers/use-keybinding");
const is_menu_item_1 = require("../Menu/is-menu-item");
const MenuDivider_1 = require("../Menu/MenuDivider");
const MenuSubItem_1 = require("../Menu/MenuSubItem");
const styles_1 = require("../Menu/styles");
const BORDER_SIZE = 1;
const container = {
paddingTop: styles_1.MENU_VERTICAL_PADDING,
paddingBottom: styles_1.MENU_VERTICAL_PADDING,
border: `${BORDER_SIZE}px solid ${colors_1.INPUT_BORDER_COLOR_UNHOVERED}`,
marginLeft: 0 - BORDER_SIZE,
overflowY: 'auto',
overflowX: 'hidden',
minWidth: 200,
maxWidth: styles_1.MAX_MENU_WIDTH,
};
const MenuContent = ({ onHide, values, preselectIndex, onNextMenu, onPreviousMenu, leaveLeftSpace, topItemCanBeUnselected, fixedHeight, }) => {
const keybindings = (0, use_keybinding_1.useKeybinding)();
const containerRef = (0, react_1.useRef)(null);
const isMobileLayout = (0, mobile_layout_1.useMobileLayout)();
const [subMenuActivated, setSubMenuActivated] = (0, react_1.useState)(false);
if (values[0].type === 'divider') {
throw new Error('first value cant be divide');
}
const [selectedItem, setSelectedItem] = (0, react_1.useState)(typeof preselectIndex === 'number' && preselectIndex >= 0
? values[preselectIndex].id
: null);
const onEscape = (0, react_1.useCallback)(() => {
onHide();
}, [onHide]);
const onItemSelected = (0, react_1.useCallback)((id) => {
setSelectedItem(id);
}, []);
const isItemSelectable = (0, react_1.useCallback)((v) => {
return v.type !== 'divider' && !v.disabled;
}, []);
const onArrowUp = (0, react_1.useCallback)(() => {
setSelectedItem((prevItem) => {
if (prevItem === null) {
return null;
}
const index = values.findIndex((val) => val.id === prevItem);
if ((topItemCanBeUnselected && index === 0) || prevItem === null) {
return null;
}
const previousItems = values.filter((v, i) => i < index && isItemSelectable(v));
if (previousItems.length > 0) {
return previousItems[previousItems.length - 1].id;
}
const firstSelectable = values.find((v) => isItemSelectable(v));
if (firstSelectable) {
return firstSelectable.id;
}
throw new Error('could not find previous item');
});
}, [topItemCanBeUnselected, values, isItemSelectable]);
const onArrowDown = (0, react_1.useCallback)(() => {
setSelectedItem((prevItem) => {
const index = values.findIndex((val) => val.id === prevItem);
const nextItem = values.find((v, i) => i > index && isItemSelectable(v));
if (nextItem) {
return nextItem.id;
}
const lastSelectable = values
.slice()
.reverse()
.find((v) => isItemSelectable(v));
if (lastSelectable) {
return lastSelectable.id;
}
throw new Error('could not find next item');
});
}, [values, isItemSelectable]);
const onEnter = (0, react_1.useCallback)(() => {
if (selectedItem === null) {
return onHide();
}
const item = values.find((i) => i.id === selectedItem);
if (!item) {
throw new Error('cannot find item');
}
if (item.type === 'divider') {
throw new Error('cannot find divider');
}
if (item.disabled) {
return;
}
if (item.subMenu) {
return setSubMenuActivated('without-mouse');
}
onHide();
item.onClick(item.id, null);
}, [onHide, selectedItem, values]);
const onArrowRight = (0, react_1.useCallback)(() => {
if (selectedItem === null) {
return onNextMenu();
}
const item = values.find((i) => i.id === selectedItem);
if (!item) {
throw new Error('cannot find item');
}
if (item.type === 'divider') {
throw new Error('cannot find divider');
}
if (!item.subMenu) {
return onNextMenu();
}
setSubMenuActivated('without-mouse');
}, [onNextMenu, selectedItem, values]);
const containerWithHeight = (0, react_1.useMemo)(() => {
const containerStyles = { ...container };
if (fixedHeight === null) {
containerStyles.maxHeight = 600;
}
else {
containerStyles.maxHeight = fixedHeight;
}
if (isMobileLayout) {
containerStyles.maxWidth = styles_1.MAX_MOBILE_MENU_WIDTH;
}
return containerStyles;
}, [fixedHeight, isMobileLayout]);
(0, react_1.useEffect)(() => {
const escapeBinding = keybindings.registerKeybinding({
event: 'keydown',
key: 'Escape',
callback: onEscape,
commandCtrlKey: false,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
const rightBinding = keybindings.registerKeybinding({
event: 'keydown',
key: 'ArrowRight',
commandCtrlKey: false,
callback: onArrowRight,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
const leftBinding = keybindings.registerKeybinding({
event: 'keydown',
commandCtrlKey: false,
key: 'ArrowLeft',
callback: onPreviousMenu,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
const downBinding = keybindings.registerKeybinding({
event: 'keydown',
key: 'ArrowDown',
commandCtrlKey: false,
callback: onArrowDown,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
const upBinding = keybindings.registerKeybinding({
event: 'keydown',
key: 'ArrowUp',
callback: onArrowUp,
commandCtrlKey: false,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
const enterBinding = keybindings.registerKeybinding({
event: 'keydown',
key: 'Enter',
callback: onEnter,
commandCtrlKey: false,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
const spaceBinding = keybindings.registerKeybinding({
event: 'keyup',
key: ' ',
callback: onEnter,
commandCtrlKey: false,
preventDefault: true,
triggerIfInputFieldFocused: false,
keepRegisteredWhenNotHighestContext: false,
});
return () => {
escapeBinding.unregister();
leftBinding.unregister();
rightBinding.unregister();
downBinding.unregister();
upBinding.unregister();
enterBinding.unregister();
spaceBinding.unregister();
};
}, [
keybindings,
onEscape,
onNextMenu,
onPreviousMenu,
onArrowDown,
onArrowUp,
onEnter,
onArrowRight,
]);
// Disable submenu if not selected
(0, react_1.useEffect)(() => {
if (!subMenuActivated) {
return;
}
if (selectedItem === null) {
return setSubMenuActivated(false);
}
const item = values.find((i) => i.id === selectedItem);
if (!item) {
// Can happen if resizing the window
return;
}
if (item.type === 'divider') {
throw new Error('should not select divider');
}
if (!item.subMenu && subMenuActivated) {
setSubMenuActivated(false);
}
}, [selectedItem, subMenuActivated, values]);
(0, react_1.useEffect)(() => {
const { current } = containerRef;
if (!current) {
return;
}
const onPointerLeave = () => {
if (subMenuActivated) {
return;
}
setSelectedItem(null);
};
current.addEventListener('pointerleave', onPointerLeave);
return () => current.removeEventListener('pointerleave', onPointerLeave);
}, [onHide, subMenuActivated]);
return (jsx_runtime_1.jsx("div", { ref: containerRef, style: containerWithHeight, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: values.map((item) => {
if (item.type === 'divider') {
return jsx_runtime_1.jsx(MenuDivider_1.MenuDivider, {}, item.id);
}
const onClick = (id, e) => {
item.onClick(id, e);
if (item.subMenu) {
return null;
}
onHide();
};
return (jsx_runtime_1.jsx(MenuSubItem_1.MenuSubItem, { selected: item.id === selectedItem, onActionChosen: onClick, onItemSelected: onItemSelected, label: item.label, id: item.id, keyHint: item.keyHint, leaveLeftSpace: leaveLeftSpace, leftItem: item.leftItem, subMenu: item.subMenu, onQuitMenu: onHide, onNextMenu: onNextMenu, subMenuActivated: subMenuActivated, setSubMenuActivated: setSubMenuActivated, disabled: item.disabled }, item.id));
}) }));
};
exports.MenuContent = MenuContent;