@octopusdeploy/design-system-components
Version:
The design systems component library.
182 lines (181 loc) • 9.89 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeprecatedMarkdownEditor = DeprecatedMarkdownEditor;
const jsx_runtime_1 = require("react/jsx-runtime");
const css_1 = require("@emotion/css");
const design_system_icons_1 = require("@octopusdeploy/design-system-icons");
const design_system_tokens_1 = require("@octopusdeploy/design-system-tokens");
const react_1 = require("react");
const Button_1 = require("../../Button");
const Link_1 = require("../../Link");
const Note_1 = require("../Note");
const Text_1 = require("../Text");
const MarkdownProvider_1 = require("./MarkdownProvider");
/**
* @deprecated This component is deprecated and will be removed in a future release.
* Deprecated: 14/07/2026
* Replacement: MarkdownEditor (packages/design-system-components/src/components/Form/MarkdownEditor/MarkdownEditor.tsx)
* Reason: As part of the forms uplift we have a new MarkdowndownEditor utilising the new form elements but wanted to ensure a manual rollout
* so we have duplicated it, deprecated the current one and created a new one using the new form elements.
*
* @remarks Continue to use this component. Only use the new one in pre-approved areas, as in #project-form-uplift if unsure before using.
*/
function DeprecatedMarkdownEditor({ onChange, onValidate, hideMarkdownToggle, restrictHeight, label, error, accessibleName, value, disabled, placeholder, textInputRef, controlsInitiallyVisible = false, autoFocus = false, bottomNoteText = null, }) {
const { inputRef, actions: markdownActions } = useTextInputMarkdownActions(value);
const handleChange = (value) => {
const valid = onValidate?.(value) ?? true;
if (valid) {
onChange?.(value);
}
};
//We currently treat the visibility for the markdown controls as a non-controlled prop. Ideally this
//would be controlled, however, that would also mean that every consumer has to manage this state.
const [showMarkdownControls, setShowMarkdownControls] = (0, react_1.useState)(controlsInitiallyVisible);
const markdownTitle = `${showMarkdownControls ? "Hide" : "Show"} markdown controls`;
const showMarkdownControlsToggle = hideMarkdownToggle ? null : ((0, jsx_runtime_1.jsx)(Button_1.Button, { label: markdownTitle, importance: "quiet", onClick: (e) => {
e.preventDefault();
setShowMarkdownControls((prev) => !prev);
} }));
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, css_1.cx)(styles.markdownEditorContainer, restrictHeight && styles.restrictedHeight), children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, css_1.cx)({ [styles.markdownEditor]: showMarkdownControls }), children: [showMarkdownControls && ((0, jsx_runtime_1.jsx)("div", { className: styles.toolbar, children: markdownActions.map(({ type, action }, index) => ((0, jsx_runtime_1.jsx)(MarkdownEditorButton, { type: type, onClick: action }, `${index}_${type}`))) })), (0, jsx_runtime_1.jsx)(Text_1.Text, { textInputRef: (text) => {
if (textInputRef) {
textInputRef(text);
}
inputRef.current = text;
}, value: value ? value : "", onChange: handleChange, error: error, label: label, placeholder: placeholder, multiline: true, accessibleName: accessibleName, minRows: showMarkdownControls ? 3 : 2, showBorder: false, className: styles.inputContainer, hideUnderline: true, autoFocus: autoFocus, disabled: disabled })] }), bottomNoteText && (0, jsx_runtime_1.jsx)(Note_1.Note, { children: bottomNoteText }), showMarkdownControls ? ((0, jsx_runtime_1.jsxs)("div", { className: styles.markdownControlsContainer, children: [showMarkdownControlsToggle, (0, jsx_runtime_1.jsx)(Link_1.ExternalLink, { href: "MasteringMarkdown", children: "Markdown instructions" })] })) : (showMarkdownControlsToggle)] }));
}
function useTextInputMarkdownActions(value) {
const inputRef = (0, react_1.useRef)(null);
const getSelection = (0, react_1.useCallback)(() => {
const selection = inputRef.current?.getSelection();
return selection ?? { start: null, end: null };
}, []);
const setInputState = (value, selectionStart, selectionEnd) => {
const selection = {
start: selectionStart,
end: selectionEnd,
};
inputRef.current?.setValueAndSelection(selection, value);
inputRef.current?.focus();
};
const getInputState = () => {
const selection = getSelection();
return new MarkdownProvider_1.MarkdownProvider(value ? value : "", selection.start ?? 0, selection.end ?? 0);
};
const bold = () => {
getInputState().insertBefore("**").insertAfter("**").emptySelectionText("Bold text").apply(setInputState);
};
const italic = () => {
getInputState().insertBefore("_").insertAfter("_").emptySelectionText("Italic text").apply(setInputState);
};
const bullet = () => {
getInputState().surroundWithNewlines().insertBefore("- ").emptySelectionText("List item").apply(setInputState);
};
const number = () => {
getInputState().surroundWithNewlines().insertBefore("1. ").emptySelectionText("List item").apply(setInputState);
};
const quote = () => {
getInputState().surroundWithNewlines().insertBefore("> ").insertAfterNewlineInSelection("> ").emptySelectionText("Quoted text").apply(setInputState);
};
const code = () => {
getInputState().surroundWithNewlines().insertBefore("```\n").insertAfter("\n```").emptySelectionText("Code").apply(setInputState);
};
const link = () => {
const url = prompt("Please enter a url for the link:", "http://");
if (url) {
getInputState().insertBefore("[").insertAfter(`](${url})`).emptySelectionText("Enter link description here").apply(setInputState);
}
};
const image = () => {
const url = prompt("Please enter a url for the image:", "http://");
if (url) {
getInputState().insertBefore("`).emptySelectionText("Enter image description here").apply(setInputState);
}
};
return {
inputRef,
actions: [
{ type: "Bold", action: bold },
{ type: "Bulleted List", action: bullet },
{ type: "Code", action: code },
{ type: "Link", action: link },
{ type: "Numeric list", action: number },
{ type: "Italic", action: italic },
{ type: "Image", action: image },
{ type: "Quotes", action: quote },
],
};
}
const styles = {
markdownEditorContainer: (0, css_1.css)({
position: "relative",
width: "100%",
}),
markdownEditor: (0, css_1.css)({
outline: `1px solid ${design_system_tokens_1.themeTokens.color.textField.border.default}`,
marginBottom: design_system_tokens_1.space[8],
"&:hover": {
outline: `2px solid ${design_system_tokens_1.themeTokens.color.textField.border.hover}`,
},
"&:has(div.MuiInputBase-root.MuiInput-root.Mui-focused)": {
outline: `2px solid ${design_system_tokens_1.themeTokens.color.textField.border.active}`,
},
"& > div:has(div.MuiInputBase-root.MuiInput-root)": {
padding: "10px 10px 0 10px",
},
"& .MuiInput-underline::before, & .MuiInput-underline:hover:not(.Mui-disabled)::before": {
borderBottom: "none",
},
"& .MuiInput-underline::after": {
borderBottom: "none",
},
"& label.MuiFormLabel-root.MuiInputLabel-root.MuiInputLabel-formControl": {
transform: "translate(0, 8px) scale(1)",
"&.Mui-focused": {
display: "none",
},
"&.MuiFormLabel-filled": {
display: "none",
},
},
"& .MuiInputBase-root.MuiInput-root.MuiInputBase-formControl.MuiInput-formControl.MuiInputBase-multiline.MuiInput-multiline": {
margin: 0,
padding: 0,
},
}),
markdownControlsContainer: (0, css_1.css)({
display: "flex",
flexDirection: "row",
alignItems: "baseline",
button: {
marginRight: design_system_tokens_1.space[8],
},
}),
restrictedHeight: (0, css_1.css)({
maxHeight: "15rem",
}),
toolbar: (0, css_1.css)({
color: design_system_tokens_1.themeTokens.color.icon.secondary,
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
backgroundColor: design_system_tokens_1.themeTokens.color.background.secondary.default,
padding: `0 ${design_system_tokens_1.space[8]}`,
gap: design_system_tokens_1.space[12],
}),
inputContainer: (0, css_1.css)({
width: "100%",
}),
};
const markdownEditorIconMap = {
"Bulleted List": (0, jsx_runtime_1.jsx)(design_system_icons_1.ListIcon, { size: 20 }),
Bold: (0, jsx_runtime_1.jsx)(design_system_icons_1.BoldIcon, { size: 20 }),
Code: (0, jsx_runtime_1.jsx)(design_system_icons_1.CodeIcon, { size: 20 }),
Italic: (0, jsx_runtime_1.jsx)(design_system_icons_1.ItalicIcon, { size: 20 }),
Link: (0, jsx_runtime_1.jsx)(design_system_icons_1.LinkIcon, { size: 20 }),
"Numeric list": (0, jsx_runtime_1.jsx)(design_system_icons_1.ListOlIcon, { size: 20 }),
Image: (0, jsx_runtime_1.jsx)(design_system_icons_1.ImageIcon, { size: 20 }),
Quotes: (0, jsx_runtime_1.jsx)(design_system_icons_1.QuotesIcon, { size: 20 }),
};
function MarkdownEditorButton({ onClick, type }) {
return (0, jsx_runtime_1.jsx)(Button_1.Button, { importance: "ghost", onClick: onClick, accessibleName: type, icon: markdownEditorIconMap[type], "aria-hidden": "true", title: type });
}