init commit

This commit is contained in:
Carlos
2026-02-21 10:33:18 +01:00
parent c863a943ed
commit 9d955bf338
9512 changed files with 2015317 additions and 1305 deletions

View File

@@ -0,0 +1,5 @@
import type { SVGProps } from 'react';
import React from 'react';
export declare const CancelButton: React.FC<SVGProps<SVGSVGElement> & {
readonly onPress: () => void;
}>;

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CancelButton = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const z_index_1 = require("../../state/z-index");
const style = {
appearance: 'none',
border: 'none',
backgroundColor: 'transparent',
color: 'white',
cursor: 'pointer',
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
};
const CancelButton = ({ onPress, ...props }) => {
const { tabIndex } = (0, z_index_1.useZIndex)();
return ((0, jsx_runtime_1.jsx)("button", { tabIndex: tabIndex, style: style, type: "button", onClick: onPress, children: (0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 320 512", ...props, children: (0, jsx_runtime_1.jsx)("path", { fill: "currentColor", d: "M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z" }) }) }));
};
exports.CancelButton = CancelButton;

View File

@@ -0,0 +1,13 @@
import type { RecastCodemod } from '@remotion/studio-shared';
import React from 'react';
export declare const CodemodFooter: React.FC<{
readonly valid: boolean;
readonly codemod: RecastCodemod;
readonly loadingNotification: React.ReactNode;
readonly successNotification: React.ReactNode;
readonly errorNotification: string;
readonly genericSubmitLabel: string;
readonly submitLabel: (options: {
relativeRootPath: string;
}) => string;
}>;

View File

@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodemodFooter = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const ShortcutHint_1 = require("../../error-overlay/remotion-overlay/ShortcutHint");
const use_keybinding_1 = require("../../helpers/use-keybinding");
const modals_1 = require("../../state/modals");
const ModalButton_1 = require("../ModalButton");
const NotificationCenter_1 = require("../Notifications/NotificationCenter");
const actions_1 = require("../RenderQueue/actions");
const layout_1 = require("../layout");
const DiffPreview_1 = require("./DiffPreview");
const CodemodFooter = ({ codemod, valid, loadingNotification, successNotification, errorNotification, genericSubmitLabel, submitLabel, }) => {
const [submitting, setSubmitting] = (0, react_1.useState)(false);
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const [codemodStatus, setCanApplyCodemod] = (0, react_1.useState)({
type: 'loading',
});
const [projectInfo, setProjectInfo] = (0, react_1.useState)(null);
(0, react_1.useEffect)(() => {
const controller = new AbortController();
(0, actions_1.getProjectInfo)(controller.signal)
.then((info) => {
setProjectInfo(info.projectInfo);
})
.catch((err) => {
(0, NotificationCenter_1.showNotification)(`Could not get project info: ${err.message}. Unable to duplicate composition`, 3000);
});
return () => {
controller.abort();
};
}, []);
const trigger = (0, react_1.useCallback)(() => {
setSubmitting(true);
setSelectedModal(null);
const notification = (0, NotificationCenter_1.showNotification)(loadingNotification, null);
(0, actions_1.applyCodemod)({
codemod,
dryRun: false,
signal: new AbortController().signal,
})
.then(() => {
notification.replaceContent(successNotification, 2000);
})
.catch((err) => {
notification.replaceContent(`${errorNotification}: ${err.message}`, 2000);
});
}, [
codemod,
errorNotification,
loadingNotification,
setSelectedModal,
successNotification,
]);
const getCanApplyCodemod = (0, react_1.useCallback)(async (signal) => {
const res = await (0, actions_1.applyCodemod)({
codemod,
dryRun: true,
signal,
});
if (res.success) {
setCanApplyCodemod({ type: 'success', diff: res.diff });
}
else {
setCanApplyCodemod({
type: 'fail',
error: res.reason,
});
}
}, [codemod]);
(0, react_1.useEffect)(() => {
const abortController = new AbortController();
let aborted = false;
getCanApplyCodemod(abortController.signal)
.then(() => undefined)
.catch((err) => {
if (aborted) {
return;
}
(0, NotificationCenter_1.showNotification)(`Cannot duplicate composition: ${err.message}`, 3000);
});
return () => {
aborted = true;
abortController.abort();
};
}, [codemodStatus, getCanApplyCodemod, setSelectedModal]);
const disabled = !valid ||
submitting ||
projectInfo === null ||
codemodStatus.type !== 'success';
const { registerKeybinding } = (0, use_keybinding_1.useKeybinding)();
(0, react_1.useEffect)(() => {
if (disabled) {
return;
}
const enter = registerKeybinding({
callback() {
trigger();
},
commandCtrlKey: true,
key: 'Enter',
event: 'keydown',
preventDefault: true,
triggerIfInputFieldFocused: true,
keepRegisteredWhenNotHighestContext: false,
});
return () => {
enter.unregister();
};
}, [disabled, registerKeybinding, trigger, valid]);
return ((0, jsx_runtime_1.jsxs)(layout_1.Row, { align: "center", children: [(0, jsx_runtime_1.jsx)(DiffPreview_1.CodemodDiffPreview, { status: codemodStatus }), (0, jsx_runtime_1.jsx)(layout_1.Flex, {}), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { block: true, x: 2 }), (0, jsx_runtime_1.jsxs)(ModalButton_1.ModalButton, { onClick: trigger, disabled: disabled, children: [projectInfo && projectInfo.relativeRootFile
? submitLabel({ relativeRootPath: projectInfo.relativeRootFile })
: genericSubmitLabel, (0, jsx_runtime_1.jsx)(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })] })] }));
};
exports.CodemodFooter = CodemodFooter;

View File

@@ -0,0 +1,30 @@
import React from 'react';
type DividerItem = {
type: 'divider';
id: string;
};
export type SubMenu = {
preselectIndex: number | false;
leaveLeftSpace: boolean;
items: ComboboxValue[];
};
export type SelectionItem = {
type: 'item';
id: string;
label: React.ReactNode;
value: string | number;
onClick: (id: string, e: React.PointerEvent | null) => void;
keyHint: string | null;
leftItem: React.ReactNode;
subMenu: SubMenu | null;
quickSwitcherLabel: string | null;
disabled?: boolean;
};
export type ComboboxValue = DividerItem | SelectionItem;
export declare const Combobox: React.FC<{
readonly values: ComboboxValue[];
readonly selectedId: string | number;
readonly style?: React.CSSProperties;
readonly title: string;
}>;
export {};

View File

@@ -0,0 +1,169 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Combobox = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const player_1 = require("@remotion/player");
const react_1 = require("react");
const react_dom_1 = __importDefault(require("react-dom"));
const colors_1 = require("../../helpers/colors");
const mobile_layout_1 = require("../../helpers/mobile-layout");
const noop_1 = require("../../helpers/noop");
const caret_1 = require("../../icons/caret");
const z_index_1 = require("../../state/z-index");
const is_menu_item_1 = require("../Menu/is-menu-item");
const portals_1 = require("../Menu/portals");
const styles_1 = require("../Menu/styles");
const layout_1 = require("../layout");
const MenuContent_1 = require("./MenuContent");
const container = {
padding: '8px 10px',
display: 'inline-block',
backgroundColor: colors_1.INPUT_BACKGROUND,
borderWidth: 1,
borderStyle: 'solid',
maxWidth: '100%',
};
const label = {
flex: 1,
overflow: 'hidden',
textOverflow: 'ellipsis',
fontSize: 14,
textAlign: 'left',
};
const Combobox = ({ values, selectedId, style: customStyle, title }) => {
const [hovered, setIsHovered] = (0, react_1.useState)(false);
const [opened, setOpened] = (0, react_1.useState)(false);
const ref = (0, react_1.useRef)(null);
const { tabIndex, currentZIndex } = (0, z_index_1.useZIndex)();
const size = player_1.PlayerInternals.useElementSize(ref, {
triggerOnWindowResize: true,
shouldApplyCssTransforms: true,
});
const refresh = size === null || size === void 0 ? void 0 : size.refresh;
const onHide = (0, react_1.useCallback)(() => {
setOpened(false);
}, []);
(0, react_1.useEffect)(() => {
const { current } = ref;
if (!current) {
return;
}
const onMouseEnter = () => setIsHovered(true);
const onMouseLeave = () => setIsHovered(false);
const onPointerDown = () => {
return setOpened((o) => {
if (!o) {
refresh === null || refresh === void 0 ? void 0 : refresh();
}
return !o;
});
};
const onClick = (e) => {
e.stopPropagation();
const isKeyboardInitiated = e.detail === 0;
if (!isKeyboardInitiated) {
return;
}
return setOpened((o) => {
if (!o) {
refresh === null || refresh === void 0 ? void 0 : refresh();
window.addEventListener('pointerup', (evt) => {
if (!(0, is_menu_item_1.isMenuItem)(evt.target)) {
setOpened(false);
}
}, {
once: true,
});
}
return !o;
});
};
current.addEventListener('mouseenter', onMouseEnter);
current.addEventListener('mouseleave', onMouseLeave);
current.addEventListener('pointerdown', onPointerDown);
current.addEventListener('click', onClick);
return () => {
current.removeEventListener('mouseenter', onMouseEnter);
current.removeEventListener('mouseleave', onMouseLeave);
current.removeEventListener('pointerdown', onPointerDown);
current.removeEventListener('click', onClick);
};
}, [refresh]);
const spaceToBottom = (0, react_1.useMemo)(() => {
const margin = 10;
if (size && opened) {
return size.windowSize.height - (size.top + size.height) - margin;
}
return 0;
}, [opened, size]);
const spaceToTop = (0, react_1.useMemo)(() => {
const margin = 10;
if (size && opened) {
return size.top - margin;
}
return 0;
}, [opened, size]);
const derivedMaxHeight = (0, react_1.useMemo)(() => {
return spaceToTop > spaceToBottom ? spaceToTop : spaceToBottom;
}, [spaceToBottom, spaceToTop]);
const isMobileLayout = (0, mobile_layout_1.useMobileLayout)();
const portalStyle = (0, react_1.useMemo)(() => {
if (!opened || !size) {
return null;
}
const spaceToRight = size.windowSize.width - size.left;
const spaceToLeft = size.left + size.width;
const minSpaceRequired = isMobileLayout
? styles_1.MAX_MOBILE_MENU_WIDTH
: styles_1.MAX_MENU_WIDTH;
const verticalLayout = spaceToTop > spaceToBottom ? 'bottom' : 'top';
const canOpenOnLeft = spaceToLeft >= minSpaceRequired;
const canOpenOnRight = spaceToRight >= minSpaceRequired;
const horizontalLayout = canOpenOnRight ? 'left' : 'right';
return {
...(verticalLayout === 'top'
? {
...styles_1.menuContainerTowardsBottom,
top: size.top + size.height,
}
: {
...styles_1.menuContainerTowardsTop,
bottom: size.windowSize.height - size.top,
}),
...(horizontalLayout === 'left'
? {
left: size.left,
}
: canOpenOnLeft
? {
right: size.windowSize.width - size.left - size.width,
}
: { left: 0 }),
};
}, [isMobileLayout, opened, size, spaceToBottom, spaceToTop]);
const selected = values.find((v) => v.id === selectedId);
const style = (0, react_1.useMemo)(() => {
return {
...container,
...(customStyle !== null && customStyle !== void 0 ? customStyle : {}),
userSelect: 'none',
WebkitUserSelect: 'none',
color: 'white',
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
borderColor: opened
? colors_1.SELECTED_BACKGROUND
: hovered
? colors_1.INPUT_BORDER_COLOR_HOVERED
: colors_1.INPUT_BORDER_COLOR_UNHOVERED,
};
}, [customStyle, hovered, opened]);
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("button", { ref: ref, title: title, tabIndex: tabIndex, type: "button", style: style, className: is_menu_item_1.MENU_INITIATOR_CLASSNAME, children: [selected ? ((0, jsx_runtime_1.jsx)("div", { title: typeof selected.label === 'string' ? selected.label : undefined, style: label, children: selected === null || selected === void 0 ? void 0 : selected.label })) : null, (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 1 }), " ", (0, jsx_runtime_1.jsx)(caret_1.CaretDown, {})] }), portalStyle
? react_dom_1.default.createPortal((0, jsx_runtime_1.jsx)("div", { style: styles_1.fullScreenOverlay, children: (0, jsx_runtime_1.jsx)("div", { style: styles_1.outerPortal, className: "css-reset", children: (0, jsx_runtime_1.jsx)(z_index_1.HigherZIndex, { onOutsideClick: onHide, onEscape: onHide, children: (0, jsx_runtime_1.jsx)("div", { style: portalStyle, children: (0, jsx_runtime_1.jsx)(MenuContent_1.MenuContent, { onNextMenu: noop_1.noop, onPreviousMenu: noop_1.noop, values: values, onHide: onHide, leaveLeftSpace: true, preselectIndex: values.findIndex((v) => selected && v.id === selected.id), topItemCanBeUnselected: false, fixedHeight: derivedMaxHeight }) }) }) }) }), (0, portals_1.getPortal)(currentZIndex))
: null] }));
};
exports.Combobox = Combobox;

View File

@@ -0,0 +1,4 @@
import React from 'react';
export declare const DeleteComposition: React.FC<{
readonly compositionId: string;
}>;

View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeleteComposition = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const styles_1 = require("../Menu/styles");
const ModalFooter_1 = require("../ModalFooter");
const ModalHeader_1 = require("../ModalHeader");
const ResolveCompositionBeforeModal_1 = require("../RenderModal/ResolveCompositionBeforeModal");
const CodemodFooter_1 = require("./CodemodFooter");
const DismissableModal_1 = require("./DismissableModal");
const content = {
padding: 16,
fontSize: 14,
flex: 1,
minWidth: 500,
};
const DeleteCompositionLoaded = ({ compositionId }) => {
const context = (0, react_1.useContext)(ResolveCompositionBeforeModal_1.ResolvedCompositionContext);
if (!context) {
throw new Error('Resolved composition context');
}
const { unresolved } = context;
const codemod = (0, react_1.useMemo)(() => {
return {
type: 'delete-composition',
idToDelete: compositionId,
};
}, [compositionId]);
const onSubmit = (0, react_1.useCallback)((e) => {
e.preventDefault();
}, []);
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.ModalHeader, { title: 'Delete composition' }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: onSubmit, children: [(0, jsx_runtime_1.jsxs)("div", { style: content, children: ["Do you want to delete the", ' ', (0, jsx_runtime_1.jsx)("code", { style: styles_1.inlineCodeSnippet, children: unresolved.durationInFrames === 1 ? `<Still>` : '<Composition>' }), ' ', "with ID ", '"', unresolved.id, '"', "?", (0, jsx_runtime_1.jsx)("br", {}), "The associated ", (0, jsx_runtime_1.jsx)("code", { style: styles_1.inlineCodeSnippet, children: "component" }), " will remain in your code."] }), (0, jsx_runtime_1.jsx)(ModalFooter_1.ModalFooterContainer, { children: (0, jsx_runtime_1.jsx)(CodemodFooter_1.CodemodFooter, { errorNotification: `Could not delete composition`, loadingNotification: 'Deleting', successNotification: `Deleted ${unresolved.id}`, genericSubmitLabel: `Delete`, submitLabel: ({ relativeRootPath }) => `Delete from ${relativeRootPath}`, codemod: codemod, valid: true }) })] })] }));
};
const DeleteComposition = ({ compositionId }) => {
return ((0, jsx_runtime_1.jsx)(DismissableModal_1.DismissableModal, { children: (0, jsx_runtime_1.jsx)(ResolveCompositionBeforeModal_1.ResolveCompositionBeforeModal, { compositionId: compositionId, children: (0, jsx_runtime_1.jsx)(DeleteCompositionLoaded, { compositionId: compositionId }) }) }));
};
exports.DeleteComposition = DeleteComposition;

View File

@@ -0,0 +1,14 @@
import type { SimpleDiff } from '@remotion/studio-shared';
import React from 'react';
export type CodemodStatus = {
type: 'loading';
} | {
type: 'success';
diff: SimpleDiff;
} | {
type: 'fail';
error: string;
};
export declare const CodemodDiffPreview: React.FC<{
readonly status: CodemodStatus;
}>;

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodemodDiffPreview = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const colors_1 = require("../../helpers/colors");
const CodemodDiffPreview = ({ status }) => {
if (status.type === 'loading') {
return null;
}
if (status.type === 'fail') {
return ((0, jsx_runtime_1.jsx)("span", { style: { color: colors_1.FAIL_COLOR, fontSize: 13, lineHeight: 1.2 }, children: status.error }));
}
return ((0, jsx_runtime_1.jsxs)("div", { style: { lineHeight: 1.2 }, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: colors_1.LIGHT_TEXT, fontSize: 13, lineHeight: 1.2 }, children: "This will edit your Root file." }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsxs)("span", { style: { color: colors_1.BLUE, fontSize: 13, lineHeight: 1.2 }, children: [status.diff.additions, " addition", status.diff.additions === 1 ? '' : 's', ","] }), ' ', (0, jsx_runtime_1.jsxs)("span", { style: {
color: colors_1.SELECTED_GUIDE,
fontSize: 13,
lineHeight: 1.2,
}, children: [status.diff.deletions, " deletion", status.diff.deletions === 1 ? '' : 's'] })] }));
};
exports.CodemodDiffPreview = CodemodDiffPreview;

View File

@@ -0,0 +1,4 @@
import React from 'react';
export declare const DismissableModal: React.FC<{
readonly children: React.ReactNode;
}>;

View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DismissableModal = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const modals_1 = require("../../state/modals");
const ModalContainer_1 = require("../ModalContainer");
const DismissableModal = ({ children }) => {
const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
const onQuit = (0, react_1.useCallback)(() => {
setSelectedModal(null);
}, [setSelectedModal]);
return ((0, jsx_runtime_1.jsx)(ModalContainer_1.ModalContainer, { onOutsideClick: onQuit, onEscape: onQuit, children: children }));
};
exports.DismissableModal = DismissableModal;

View File

@@ -0,0 +1,6 @@
import React from 'react';
export type CompType = 'composition' | 'still';
export declare const DuplicateComposition: React.FC<{
readonly compositionId: string;
readonly compositionType: CompType;
}>;

View File

@@ -0,0 +1,180 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DuplicateComposition = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const remotion_1 = require("remotion");
const validate_new_comp_data_1 = require("../../helpers/validate-new-comp-data");
const ModalFooter_1 = require("../ModalFooter");
const ModalHeader_1 = require("../ModalHeader");
const ResolveCompositionBeforeModal_1 = require("../RenderModal/ResolveCompositionBeforeModal");
const layout_1 = require("../RenderModal/layout");
const layout_2 = require("../layout");
const CodemodFooter_1 = require("./CodemodFooter");
const ComboBox_1 = require("./ComboBox");
const DismissableModal_1 = require("./DismissableModal");
const InputDragger_1 = require("./InputDragger");
const NewCompDuration_1 = require("./NewCompDuration");
const RemInput_1 = require("./RemInput");
const ValidationMessage_1 = require("./ValidationMessage");
const content = {
padding: 12,
paddingRight: 12,
flex: 1,
fontSize: 13,
minWidth: 500,
};
const comboBoxStyle = {
width: 190,
};
const DuplicateCompositionLoaded = ({ initialType }) => {
const context = (0, react_1.useContext)(ResolveCompositionBeforeModal_1.ResolvedCompositionContext);
if (!context) {
throw new Error('Resolved composition context');
}
const { resolved, unresolved } = context;
const [initialCompType] = (0, react_1.useState)(initialType);
const hadDimensionsDefined = unresolved.width && unresolved.height;
const hadFpsDefined = unresolved.fps !== undefined;
const hadDurationDefined = unresolved.durationInFrames !== undefined;
const [selectedFrameRate, setFrameRate] = (0, react_1.useState)(resolved.result.fps);
const { compositions } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
const [type, setType] = (0, react_1.useState)(initialCompType);
const [newId, setName] = (0, react_1.useState)(() => {
var _a;
const numberAtEnd = (_a = resolved.result.id.match(/([0-9]+)$/)) === null || _a === void 0 ? void 0 : _a[0];
let prefix = numberAtEnd ? Number(numberAtEnd) : 1;
const initialName = resolved.result.id.replace(/([0-9]+)$/, '');
let currentName = initialName;
while (true) {
currentName = initialName + prefix;
const err = (0, validate_new_comp_data_1.validateCompositionName)(currentName, compositions);
if (!err) {
break;
}
prefix++;
}
return currentName;
});
const [size, setSize] = (0, react_1.useState)(() => ({
width: resolved.result.width,
height: resolved.result.height,
}));
const [durationInFrames, setDurationInFrames] = (0, react_1.useState)(resolved.result.durationInFrames);
const onTypeChanged = (0, react_1.useCallback)((newType) => {
setType(newType);
}, []);
const onWidthChanged = (0, react_1.useCallback)((newValue) => {
setSize((s) => {
const { height } = s;
const newWidth = Number(newValue);
return {
height,
width: newWidth,
};
});
}, []);
const onWidthDirectlyChanged = (0, react_1.useCallback)((newWidth) => {
setSize((s) => {
const { height } = s;
return {
height,
width: newWidth,
};
});
}, []);
const onHeightDirectlyChanged = (0, react_1.useCallback)((newHeight) => {
setSize((s) => {
const { width } = s;
return {
width,
height: newHeight,
};
});
}, []);
const onHeightChanged = (0, react_1.useCallback)((newValue) => {
setSize((s) => {
const { width } = s;
const newHeight = Number(newValue);
return {
width,
height: newHeight,
};
});
}, []);
const onNameChange = (0, react_1.useCallback)((e) => {
setName(e.target.value);
}, []);
const onTextFpsChange = (0, react_1.useCallback)((newFps) => {
setFrameRate(Number(newFps));
}, []);
const onFpsChange = (0, react_1.useCallback)((newFps) => {
setFrameRate(newFps);
}, []);
const compNameErrMessage = (0, validate_new_comp_data_1.validateCompositionName)(newId, compositions);
const compWidthErrMessage = (0, validate_new_comp_data_1.validateCompositionDimension)('Width', size.width);
const compHeightErrMessage = (0, validate_new_comp_data_1.validateCompositionDimension)('Height', size.height);
const typeValues = (0, react_1.useMemo)(() => {
return [
{
id: 'composition',
keyHint: null,
label: '<Composition />',
leftItem: null,
onClick: () => onTypeChanged('composition'),
subMenu: null,
type: 'item',
value: 'composition',
quickSwitcherLabel: null,
},
{
id: 'still',
keyHint: null,
label: '<Still />',
leftItem: null,
onClick: () => onTypeChanged('still'),
subMenu: null,
type: 'item',
value: 'still',
quickSwitcherLabel: null,
},
];
}, [onTypeChanged]);
const valid = compNameErrMessage === null &&
compWidthErrMessage === null &&
compHeightErrMessage === null;
const codemod = (0, react_1.useMemo)(() => {
return {
type: 'duplicate-composition',
idToDuplicate: resolved.result.id,
newDurationInFrames: hadDurationDefined ? Number(durationInFrames) : null,
newFps: hadFpsDefined ? Number(selectedFrameRate) : null,
newHeight: hadDimensionsDefined ? Number(size.height) : null,
newWidth: hadDimensionsDefined ? Number(size.width) : null,
newId,
tag: type === 'still' ? 'Still' : 'Composition',
};
}, [
durationInFrames,
hadDimensionsDefined,
hadDurationDefined,
hadFpsDefined,
newId,
resolved.result.id,
selectedFrameRate,
size.height,
size.width,
type,
]);
const onSubmit = (0, react_1.useCallback)((e) => {
e.preventDefault();
}, []);
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.ModalHeader, { title: `Duplicate ${resolved.result.id}` }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: onSubmit, children: [(0, jsx_runtime_1.jsxs)("div", { style: content, children: [initialCompType === 'composition' ? (
// We allow converting from a composition to a still, but
// not the other way around
(0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "Type" }), (0, jsx_runtime_1.jsx)("div", { style: layout_1.rightRow, children: (0, jsx_runtime_1.jsx)(ComboBox_1.Combobox, { title: "Type of composition", style: comboBoxStyle, values: typeValues, selectedId: type }) })] })) : null, (0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "ID" }), (0, jsx_runtime_1.jsx)("div", { style: layout_1.rightRow, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput, { value: newId, onChange: onNameChange, type: "text", autoFocus: true, placeholder: "Composition ID", status: "ok", rightAlign: true }), compNameErrMessage ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_2.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-start", message: compNameErrMessage, type: "error" })] })) : null] }) })] }), hadDimensionsDefined ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "Width" }), (0, jsx_runtime_1.jsxs)("div", { style: layout_1.rightRow, children: [(0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { type: "number", value: size.width, placeholder: "Width", onTextChange: onWidthChanged, name: "width", step: 2, min: 2, required: true, status: "ok", formatter: (w) => `${w}px`, max: 100000000, onValueChange: onWidthDirectlyChanged, rightAlign: false }), compWidthErrMessage ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_2.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-start", message: compWidthErrMessage, type: "error" })] })) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "Height" }), (0, jsx_runtime_1.jsxs)("div", { style: layout_1.rightRow, children: [(0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { type: "number", value: size.height, onTextChange: onHeightChanged, placeholder: "Height", name: "height", step: 2, required: true, formatter: (h) => `${h}px`, min: 2, status: "ok", max: 100000000, onValueChange: onHeightDirectlyChanged, rightAlign: false }), compHeightErrMessage ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_2.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-start", message: compHeightErrMessage, type: "error" })] })) : null] })] })] })) : null, type === 'composition' && hadDurationDefined ? ((0, jsx_runtime_1.jsx)(NewCompDuration_1.NewCompDuration, { durationInFrames: durationInFrames, setDurationInFrames: setDurationInFrames })) : null, type === 'composition' && hadFpsDefined ? ((0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "FPS" }), (0, jsx_runtime_1.jsx)("div", { style: layout_1.rightRow, children: (0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { type: "number", value: selectedFrameRate, onTextChange: onTextFpsChange, placeholder: "Frame rate (fps)", name: "fps", min: 1, required: true, status: "ok", max: 240, step: 0.01, onValueChange: onFpsChange, rightAlign: false }) })] })) : null] }), (0, jsx_runtime_1.jsx)(ModalFooter_1.ModalFooterContainer, { children: (0, jsx_runtime_1.jsx)(CodemodFooter_1.CodemodFooter, { loadingNotification: 'Duplicating...', errorNotification: 'Could not duplicate composition', successNotification: `Duplicated ${unresolved.id} as ${newId}`, genericSubmitLabel: 'Duplicate', submitLabel: ({ relativeRootPath }) => `Add to ${relativeRootPath}`, codemod: codemod, valid: valid }) })] })] }));
};
const DuplicateComposition = ({ compositionId, compositionType }) => {
return ((0, jsx_runtime_1.jsx)(DismissableModal_1.DismissableModal, { children: (0, jsx_runtime_1.jsx)(ResolveCompositionBeforeModal_1.ResolveCompositionBeforeModal, { compositionId: compositionId, children: (0, jsx_runtime_1.jsx)(DuplicateCompositionLoaded, { initialType: compositionType }) }) }));
};
exports.DuplicateComposition = DuplicateComposition;

View File

@@ -0,0 +1,10 @@
import type { InputHTMLAttributes } from 'react';
import React from 'react';
import type { RemInputStatus } from './RemInput';
export declare const InputDragger: React.ForwardRefExoticComponent<InputHTMLAttributes<HTMLInputElement> & {
readonly onValueChange: (newVal: number) => void;
readonly onTextChange: (newVal: string) => void;
readonly status: RemInputStatus;
readonly formatter?: (str: number | string) => string;
readonly rightAlign: boolean;
} & React.RefAttributes<HTMLButtonElement>>;

View File

@@ -0,0 +1,156 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputDragger = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const remotion_1 = require("remotion");
const colors_1 = require("../../helpers/colors");
const noop_1 = require("../../helpers/noop");
const input_dragger_click_lock_1 = require("../../state/input-dragger-click-lock");
const z_index_1 = require("../../state/z-index");
const RemInput_1 = require("./RemInput");
const isInt = (num) => {
return num % 1 === 0;
};
const InputDraggerForwardRefFn = ({ onValueChange, min: _min, max: _max, step: _step, value, onTextChange, formatter = (q) => String(q), status, rightAlign, ...props }, ref) => {
const [inputFallback, setInputFallback] = (0, react_1.useState)(false);
const fallbackRef = (0, react_1.useRef)(null);
const style = (0, react_1.useMemo)(() => {
return {
...RemInput_1.inputBaseStyle,
backgroundColor: 'transparent',
borderColor: 'transparent',
};
}, []);
const span = (0, react_1.useMemo)(() => ({
borderBottom: '1px dotted ' + colors_1.BLUE,
paddingBottom: 1,
color: colors_1.BLUE,
cursor: 'ew-resize',
userSelect: 'none',
WebkitUserSelect: 'none',
fontSize: 13,
fontVariantNumeric: 'tabular-nums',
}), []);
const onClick = (0, react_1.useCallback)((e) => {
if (!(0, input_dragger_click_lock_1.getClickLock)()) {
e.stopPropagation();
}
if ((0, input_dragger_click_lock_1.getClickLock)()) {
return;
}
setInputFallback(true);
}, []);
const onEscape = (0, react_1.useCallback)(() => {
setInputFallback(false);
}, []);
const onBlur = (0, react_1.useCallback)(() => {
if (!fallbackRef.current) {
return;
}
const newValue = fallbackRef.current.value;
if (newValue.trim() === '') {
onEscape();
return;
}
if (fallbackRef.current.checkValidity()) {
onTextChange === null || onTextChange === void 0 ? void 0 : onTextChange(newValue);
setInputFallback(false);
}
else {
fallbackRef.current.reportValidity();
}
}, [onEscape, onTextChange]);
const onKeyPress = (0, react_1.useCallback)((e) => {
var _a;
if (e.key === 'Enter') {
(_a = fallbackRef.current) === null || _a === void 0 ? void 0 : _a.blur();
}
}, []);
const roundToStep = (val, stepSize) => {
const factor = 1 / stepSize;
return Math.ceil(val * factor) / factor;
};
const onPointerDown = (0, react_1.useCallback)((e) => {
const { pageX, pageY, button } = e;
if (button !== 0) {
return;
}
const moveListener = (ev) => {
const xDistance = ev.pageX - pageX;
const distanceFromStart = Math.sqrt(xDistance ** 2 + (ev.pageY - pageY) ** 2);
const step = Number(_step !== null && _step !== void 0 ? _step : 1);
const min = Number(_min !== null && _min !== void 0 ? _min : 0);
const max = Number(_max !== null && _max !== void 0 ? _max : Infinity);
if (distanceFromStart > 4) {
(0, input_dragger_click_lock_1.setClickLock)(true);
}
const diff = (0, remotion_1.interpolate)(xDistance, [-5, -4, 0, 4, 5], [-step, 0, 0, 0, step]);
const newValue = Math.min(max, Math.max(min, Number(value) + diff));
const roundedToStep = roundToStep(newValue, step);
onValueChange(roundedToStep);
};
window.addEventListener('mousemove', moveListener);
window.addEventListener('pointerup', () => {
window.removeEventListener('mousemove', moveListener);
setTimeout(() => {
(0, input_dragger_click_lock_1.setClickLock)(false);
}, 2);
}, {
once: true,
});
}, [_step, _min, _max, value, onValueChange]);
(0, react_1.useEffect)(() => {
var _a;
if (inputFallback) {
(_a = fallbackRef.current) === null || _a === void 0 ? void 0 : _a.select();
}
}, [inputFallback]);
const deriveStep = (0, react_1.useMemo)(() => {
if (_step !== undefined) {
return _step;
}
if (typeof _min === 'number' && isInt(_min)) {
return 1;
}
return 0.0001;
}, [_min, _step]);
if (inputFallback) {
return ((0, jsx_runtime_1.jsx)(z_index_1.HigherZIndex, { onEscape: onEscape, onOutsideClick: noop_1.noop, children: (0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput, { ref: fallbackRef, autoFocus: true, onKeyPress: onKeyPress, onBlur: onBlur, min: _min, max: _max, step: deriveStep, defaultValue: value, status: status, pattern: '[0-9]*[.]?[0-9]*', rightAlign: rightAlign, ...props }) }));
}
return ((0, jsx_runtime_1.jsx)("button", { ref: ref, type: "button", style: style, onClick: onClick, onPointerDown: onPointerDown, children: (0, jsx_runtime_1.jsx)("span", { style: span, children: formatter(value) }) }));
};
exports.InputDragger = react_1.default.forwardRef(InputDraggerForwardRefFn);

View File

@@ -0,0 +1,12 @@
import React from 'react';
import type { ComboboxValue } from './ComboBox';
export declare const MenuContent: React.FC<{
readonly values: ComboboxValue[];
readonly onHide: () => void;
readonly onNextMenu: () => void;
readonly onPreviousMenu: () => void;
readonly leaveLeftSpace: boolean;
readonly preselectIndex: false | number;
readonly topItemCanBeUnselected: boolean;
readonly fixedHeight: number | null;
}>;

View File

@@ -0,0 +1,261 @@
"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 MenuDivider_1 = require("../Menu/MenuDivider");
const MenuSubItem_1 = require("../Menu/MenuSubItem");
const is_menu_item_1 = require("../Menu/is-menu-item");
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 ((0, 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 (0, jsx_runtime_1.jsx)(MenuDivider_1.MenuDivider, {}, item.id);
}
const onClick = (id, e) => {
item.onClick(id, e);
if (item.subMenu) {
return null;
}
onHide();
};
return ((0, 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;

View File

@@ -0,0 +1,5 @@
import React from 'react';
export declare const NewCompDuration: React.FC<{
readonly durationInFrames: number;
readonly setDurationInFrames: React.Dispatch<React.SetStateAction<number>>;
}>;

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NewCompDuration = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const validate_new_comp_data_1 = require("../../helpers/validate-new-comp-data");
const layout_1 = require("../RenderModal/layout");
const layout_2 = require("../layout");
const InputDragger_1 = require("./InputDragger");
const ValidationMessage_1 = require("./ValidationMessage");
const NewCompDuration = ({ durationInFrames, setDurationInFrames }) => {
const onDurationInFramesChanged = (0, react_1.useCallback)((newValue) => {
setDurationInFrames(Number(newValue));
}, [setDurationInFrames]);
const onDurationChangedDirectly = (0, react_1.useCallback)((newVal) => {
setDurationInFrames(newVal);
}, [setDurationInFrames]);
const compDurationErrMessage = (0, validate_new_comp_data_1.validateCompositionDuration)(durationInFrames);
return ((0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "Duration in frames" }), (0, jsx_runtime_1.jsxs)("div", { style: layout_1.rightRow, children: [(0, jsx_runtime_1.jsx)(InputDragger_1.InputDragger, { type: "number", value: durationInFrames, onTextChange: onDurationInFramesChanged, placeholder: "Duration (frames)", name: "durationInFrames", min: 1, step: 1, required: true, status: "ok",
// Hitting Promise.all() limit in Chrome
max: 300000, onValueChange: onDurationChangedDirectly, rightAlign: false }), compDurationErrMessage ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_2.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-start", message: compDurationErrMessage, type: "error" })] })) : null] })] }));
};
exports.NewCompDuration = NewCompDuration;

View File

@@ -0,0 +1,17 @@
import type { PropsWithChildren } from 'react';
import React from 'react';
export type RemInputStatus = 'error' | 'warning' | 'ok';
type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> & {
readonly status: RemInputStatus;
readonly rightAlign: boolean;
};
export declare const INPUT_HORIZONTAL_PADDING = 8;
export declare const RightAlignInput: React.FC<PropsWithChildren>;
export declare const inputBaseStyle: React.CSSProperties;
export declare const getInputBorderColor: ({ status, isFocused, isHovered, }: {
status: "error" | "warning" | "ok";
isFocused: boolean;
isHovered: boolean;
}) => "hsla(0, 0%, 100%, 0.15)" | "rgba(0, 0, 0, 0.6)" | "rgba(255, 255, 255, 0.05)" | "#ff3232" | "#f1c40f";
export declare const RemotionInput: React.ForwardRefExoticComponent<Omit<Props, "ref"> & React.RefAttributes<HTMLInputElement>>;
export {};

View File

@@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemotionInput = exports.getInputBorderColor = exports.inputBaseStyle = exports.RightAlignInput = exports.INPUT_HORIZONTAL_PADDING = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const colors_1 = require("../../helpers/colors");
const z_index_1 = require("../../state/z-index");
exports.INPUT_HORIZONTAL_PADDING = 8;
const aligner = {
marginRight: -exports.INPUT_HORIZONTAL_PADDING,
};
const RightAlignInput = ({ children }) => {
return (0, jsx_runtime_1.jsx)("div", { style: aligner, children: children });
};
exports.RightAlignInput = RightAlignInput;
exports.inputBaseStyle = {
padding: `${exports.INPUT_HORIZONTAL_PADDING}px 10px`,
color: 'white',
borderStyle: 'solid',
borderWidth: 1,
fontSize: 14,
};
const getInputBorderColor = ({ status, isFocused, isHovered, }) => status === 'warning'
? colors_1.WARNING_COLOR
: status === 'error'
? colors_1.FAIL_COLOR
: isFocused
? colors_1.SELECTED_BACKGROUND
: isHovered
? colors_1.INPUT_BORDER_COLOR_HOVERED
: colors_1.INPUT_BORDER_COLOR_UNHOVERED;
exports.getInputBorderColor = getInputBorderColor;
const RemInputForwardRef = ({ status, rightAlign, ...props }, ref) => {
const [isFocused, setIsFocused] = (0, react_1.useState)(false);
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
const inputRef = (0, react_1.useRef)(null);
const { tabIndex } = (0, z_index_1.useZIndex)();
const style = (0, react_1.useMemo)(() => {
var _a;
return {
backgroundColor: colors_1.INPUT_BACKGROUND,
...exports.inputBaseStyle,
width: '100%',
borderColor: (0, exports.getInputBorderColor)({ isFocused, isHovered, status }),
textAlign: rightAlign ? 'right' : 'left',
...((_a = props.style) !== null && _a !== void 0 ? _a : {}),
};
}, [isFocused, isHovered, rightAlign, props.style, status]);
(0, react_1.useImperativeHandle)(ref, () => {
return inputRef.current;
}, []);
(0, react_1.useEffect)(() => {
if (!inputRef.current) {
return;
}
const { current } = inputRef;
const onFocus = () => setIsFocused(true);
const onBlur = () => setIsFocused(false);
const onMouseEnter = () => setIsHovered(true);
const onMouseLeave = () => setIsHovered(false);
current.addEventListener('focus', onFocus);
current.addEventListener('blur', onBlur);
current.addEventListener('mouseenter', onMouseEnter);
current.addEventListener('mouseleave', onMouseLeave);
return () => {
current.removeEventListener('focus', onFocus);
current.removeEventListener('blur', onBlur);
current.removeEventListener('mouseenter', onMouseEnter);
current.removeEventListener('mouseleave', onMouseLeave);
};
}, [inputRef]);
return (0, jsx_runtime_1.jsx)("input", { ref: inputRef, tabIndex: tabIndex, ...props, style: style });
};
exports.RemotionInput = (0, react_1.forwardRef)(RemInputForwardRef);

View File

@@ -0,0 +1,8 @@
import React from 'react';
import type { RemInputStatus } from './RemInput';
type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> & {
readonly status: RemInputStatus;
readonly name: string;
};
export declare const RemInputTypeColor: React.ForwardRefExoticComponent<Omit<Props, "ref"> & React.RefAttributes<HTMLInputElement>>;
export {};

View File

@@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemInputTypeColor = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const colors_1 = require("../../helpers/colors");
const z_index_1 = require("../../state/z-index");
const RemInput_1 = require("./RemInput");
const inputBaseStyle = {
padding: 0,
borderStyle: 'solid',
borderWidth: 1,
};
const RemInputTypeColorForwardRef = ({ status, ...props }, ref) => {
const [isFocused, setIsFocused] = (0, react_1.useState)(false);
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
const inputRef = (0, react_1.useRef)(null);
const { tabIndex } = (0, z_index_1.useZIndex)();
const style = (0, react_1.useMemo)(() => {
var _a;
return {
backgroundColor: colors_1.INPUT_BACKGROUND,
...inputBaseStyle,
borderColor: (0, RemInput_1.getInputBorderColor)({ isFocused, isHovered, status }),
...((_a = props.style) !== null && _a !== void 0 ? _a : {}),
};
}, [isFocused, isHovered, props.style, status]);
(0, react_1.useImperativeHandle)(ref, () => {
return inputRef.current;
}, []);
(0, react_1.useEffect)(() => {
if (!inputRef.current) {
return;
}
const { current } = inputRef;
const onFocus = () => setIsFocused(true);
const onBlur = () => setIsFocused(false);
const onMouseEnter = () => setIsHovered(true);
const onMouseLeave = () => setIsHovered(false);
current.addEventListener('focus', onFocus);
current.addEventListener('blur', onBlur);
current.addEventListener('mouseenter', onMouseEnter);
current.addEventListener('mouseleave', onMouseLeave);
return () => {
current.removeEventListener('focus', onFocus);
current.removeEventListener('blur', onBlur);
current.removeEventListener('mouseenter', onMouseEnter);
current.removeEventListener('mouseleave', onMouseLeave);
};
}, [inputRef]);
return ((0, jsx_runtime_1.jsx)("input", { ref: inputRef, type: "color", tabIndex: tabIndex, ...props, name: props.name, style: style }));
};
exports.RemInputTypeColor = (0, react_1.forwardRef)(RemInputTypeColorForwardRef);

View File

@@ -0,0 +1,6 @@
import React from 'react';
type Props = React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement> & {
readonly status: 'error' | 'warning' | 'ok';
};
export declare const RemTextarea: React.ForwardRefExoticComponent<Omit<Props, "ref"> & React.RefAttributes<HTMLTextAreaElement>>;
export {};

View File

@@ -0,0 +1,92 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemTextarea = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const colors_1 = require("../../helpers/colors");
const z_index_1 = require("../../state/z-index");
const is_menu_item_1 = require("../Menu/is-menu-item");
const RemInput_1 = require("../NewComposition/RemInput");
const inputBaseStyle = {
padding: `${RemInput_1.INPUT_HORIZONTAL_PADDING}px 10px`,
color: 'white',
borderStyle: 'solid',
borderWidth: 1,
fontSize: 14,
resize: 'none',
overflowX: 'hidden',
};
const RemTextareaFRFunction = ({ status, ...props }, ref) => {
const [isFocused, setIsFocused] = (0, react_1.useState)(false);
const [isHovered, setIsHovered] = (0, react_1.useState)(false);
const inputRef = (0, react_1.useRef)(null);
const { tabIndex } = (0, z_index_1.useZIndex)();
(0, react_1.useImperativeHandle)(ref, () => {
return inputRef.current;
}, []);
const style = (0, react_1.useMemo)(() => {
var _a;
return {
backgroundColor: colors_1.INPUT_BACKGROUND,
...inputBaseStyle,
width: '100%',
borderColor: (0, RemInput_1.getInputBorderColor)({ isFocused, isHovered, status }),
...((_a = props.style) !== null && _a !== void 0 ? _a : {}),
};
}, [isFocused, isHovered, props.style, status]);
(0, react_1.useEffect)(() => {
if (!inputRef.current) {
return;
}
const { current } = inputRef;
const onFocus = () => setIsFocused(true);
const onBlur = () => setIsFocused(false);
const onMouseEnter = () => setIsHovered(true);
const onMouseLeave = () => setIsHovered(false);
const onKeyDown = (e) => {
if (!inputRef.current) {
return;
}
if (inputRef.current !== document.activeElement) {
return;
}
if (e.code === 'Tab') {
e.preventDefault();
// Always match up with value in JSON.stringify(content, null, 2)
document.execCommand('insertText', false, ' '.repeat(2));
}
if (e.code === 'Enter') {
e.preventDefault();
const { selectionStart, selectionEnd, value } = inputRef.current;
if (selectionStart !== selectionEnd) {
return;
}
let prevNewline = selectionStart;
for (let i = selectionStart - 1; i >= 0; i--) {
if (value[i] === '\n') {
break;
}
prevNewline = i;
}
const currentLine = value.substring(prevNewline, selectionStart);
const trimmed = currentLine.trim();
const difference = currentLine.length - trimmed.length;
document.execCommand('insertText', false, '\n' + ' '.repeat(difference));
}
};
current.addEventListener('focus', onFocus);
current.addEventListener('blur', onBlur);
current.addEventListener('mouseenter', onMouseEnter);
current.addEventListener('mouseleave', onMouseLeave);
current.addEventListener('keydown', onKeyDown);
return () => {
current.removeEventListener('focus', onFocus);
current.removeEventListener('blur', onBlur);
current.removeEventListener('mouseenter', onMouseEnter);
current.removeEventListener('mouseleave', onMouseLeave);
current.removeEventListener('keydown', onKeyDown);
};
}, [inputRef]);
return ((0, jsx_runtime_1.jsx)("textarea", { ref: inputRef, tabIndex: tabIndex, ...props, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, style: style }));
};
exports.RemTextarea = (0, react_1.forwardRef)(RemTextareaFRFunction);

View File

@@ -0,0 +1,4 @@
import React from 'react';
export declare const RenameComposition: React.FC<{
readonly compositionId: string;
}>;

View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RenameComposition = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const remotion_1 = require("remotion");
const validate_new_comp_data_1 = require("../../helpers/validate-new-comp-data");
const ModalFooter_1 = require("../ModalFooter");
const ModalHeader_1 = require("../ModalHeader");
const ResolveCompositionBeforeModal_1 = require("../RenderModal/ResolveCompositionBeforeModal");
const layout_1 = require("../RenderModal/layout");
const layout_2 = require("../layout");
const CodemodFooter_1 = require("./CodemodFooter");
const DismissableModal_1 = require("./DismissableModal");
const RemInput_1 = require("./RemInput");
const ValidationMessage_1 = require("./ValidationMessage");
const content = {
padding: 12,
paddingRight: 12,
flex: 1,
fontSize: 13,
minWidth: 500,
};
const RenameCompositionLoaded = () => {
const context = (0, react_1.useContext)(ResolveCompositionBeforeModal_1.ResolvedCompositionContext);
if (!context) {
throw new Error('Resolved composition context');
}
const { resolved } = context;
const { compositions } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
const [newId, setName] = (0, react_1.useState)(() => {
return resolved.result.id;
});
const onNameChange = (0, react_1.useCallback)((e) => {
setName(e.target.value);
}, []);
const compNameErrMessage = newId === resolved.result.id
? null
: (0, validate_new_comp_data_1.validateCompositionName)(newId, compositions);
const valid = compNameErrMessage === null && resolved.result.id !== newId;
const codemod = (0, react_1.useMemo)(() => {
return {
type: 'rename-composition',
idToRename: resolved.result.id,
newId,
};
}, [newId, resolved.result.id]);
const onSubmit = (0, react_1.useCallback)((e) => {
e.preventDefault();
}, []);
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ModalHeader_1.ModalHeader, { title: `Rename ${resolved.result.id}` }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: onSubmit, children: [(0, jsx_runtime_1.jsx)("div", { style: content, children: (0, jsx_runtime_1.jsxs)("div", { style: layout_1.optionRow, children: [(0, jsx_runtime_1.jsx)("div", { style: layout_1.label, children: "ID" }), (0, jsx_runtime_1.jsx)("div", { style: layout_1.rightRow, children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(RemInput_1.RemotionInput, { value: newId, onChange: onNameChange, type: "text", autoFocus: true, placeholder: "Composition ID", status: "ok", rightAlign: true }), compNameErrMessage ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(layout_2.Spacing, { y: 1, block: true }), (0, jsx_runtime_1.jsx)(ValidationMessage_1.ValidationMessage, { align: "flex-start", message: compNameErrMessage, type: "error" })] })) : null] }) })] }) }), (0, jsx_runtime_1.jsx)(ModalFooter_1.ModalFooterContainer, { children: (0, jsx_runtime_1.jsx)(CodemodFooter_1.CodemodFooter, { loadingNotification: 'Renaming...', errorNotification: 'Could not rename composition', successNotification: `Renamed to ${newId}`, genericSubmitLabel: 'Rename', submitLabel: ({ relativeRootPath }) => `Modify ${relativeRootPath}`, codemod: codemod, valid: valid }) })] })] }));
};
const RenameComposition = ({ compositionId }) => {
return ((0, jsx_runtime_1.jsx)(DismissableModal_1.DismissableModal, { children: (0, jsx_runtime_1.jsx)(ResolveCompositionBeforeModal_1.ResolveCompositionBeforeModal, { compositionId: compositionId, children: (0, jsx_runtime_1.jsx)(RenameCompositionLoaded, {}) }) }));
};
exports.RenameComposition = RenameComposition;

View File

@@ -0,0 +1,8 @@
import type { SVGProps } from 'react';
import React from 'react';
export declare const WarningTriangle: React.FC<SVGProps<SVGSVGElement>>;
export declare const ValidationMessage: React.FC<{
readonly message: string;
readonly align: 'flex-start' | 'flex-end';
readonly type: 'warning' | 'error';
}>;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationMessage = exports.WarningTriangle = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const colors_1 = require("../../helpers/colors");
const layout_1 = require("../layout");
const WarningTriangle = (props) => {
return ((0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 576 512", ...props, children: (0, jsx_runtime_1.jsx)("path", { d: "M248.747 204.705l6.588 112c.373 6.343 5.626 11.295 11.979 11.295h41.37a12 12 0 0 0 11.979-11.295l6.588-112c.405-6.893-5.075-12.705-11.979-12.705h-54.547c-6.903 0-12.383 5.812-11.978 12.705zM330 384c0 23.196-18.804 42-42 42s-42-18.804-42-42 18.804-42 42-42 42 18.804 42 42zm-.423-360.015c-18.433-31.951-64.687-32.009-83.154 0L6.477 440.013C-11.945 471.946 11.118 512 48.054 512H527.94c36.865 0 60.035-39.993 41.577-71.987L329.577 23.985zM53.191 455.002L282.803 57.008c2.309-4.002 8.085-4.002 10.394 0l229.612 397.993c2.308 4-.579 8.998-5.197 8.998H58.388c-4.617.001-7.504-4.997-5.197-8.997z" }) }));
};
exports.WarningTriangle = WarningTriangle;
const style = {
width: 12,
height: 12,
flexShrink: 0,
};
const container = {
maxWidth: 500,
};
const label = {
fontSize: 13,
color: 'white',
fontFamily: 'sans-serif',
};
const ValidationMessage = ({ message, align, type }) => {
const finalStyle = (0, react_1.useMemo)(() => {
return {
...style,
fill: type === 'warning' ? colors_1.WARNING_COLOR : colors_1.FAIL_COLOR,
};
}, [type]);
return ((0, jsx_runtime_1.jsx)("div", { style: container, children: (0, jsx_runtime_1.jsxs)(layout_1.Row, { align: "center", justify: align, children: [(0, jsx_runtime_1.jsx)(exports.WarningTriangle, { style: finalStyle }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { x: 1 }), (0, jsx_runtime_1.jsx)("div", { style: label, children: message })] }) }));
};
exports.ValidationMessage = ValidationMessage;

View File

@@ -0,0 +1 @@
export declare const aspectRatio: (width: number, height: number) => string;

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.aspectRatio = void 0;
function gcd_two_numbers(x, y) {
x = Math.abs(x);
y = Math.abs(y);
while (y) {
const t = y;
y = x % y;
x = t;
}
return x;
}
const aspectRatio = (width, height) => {
const commonDivisor = gcd_two_numbers(width, height);
const widthDivisor = width / commonDivisor;
const heightDivisor = height / commonDivisor;
if (widthDivisor < 100) {
return widthDivisor + ':' + heightDivisor;
}
return (widthDivisor / heightDivisor).toFixed(2);
};
exports.aspectRatio = aspectRatio;