codice
Version:
Codice is a slim React components suite for code editing and displaying story. It provides an editor component and a code block component with syntax highlighting. Styling customization is enabled through CSS variables and HTML attributes.
162 lines (157 loc) • 4.81 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { forwardRef, useState, useRef, useEffect } from 'react';
import { f as fontSizeCss, S as ScopedStyle, C as CodeHeader, a as Code, g as getExtension } from './code-12s-Dto1Jf_T.js';
const R = `:scope[data-codice-editor]`;
const EDITOR_CSS = `\
${R} {
--codice-text-color: transparent;
--codice-background-color: transparent;
--codice-caret-color: inherit;
position: relative;
overflow-y: scroll;
display: flex;
flex-direction: column;
justify-content: stretch;
scrollbar-width: none;
}
${R} textarea {
padding: calc(var(--codice-code-padding) * 0.75) calc(var(--codice-code-padding) * 0.5);
}
${R} code,
${R} textarea {
font-family: var(--codice-font-family);
line-break: anywhere;
overflow-wrap: break-word;
scrollbar-width: none;
line-height: 1.5;
font-size: var(--codice-font-size);
caret-color: var(--codice-caret-color);
border: none;
outline: none;
width: 100%;
}
${R} code {
display: inline-block;
width: 100%;
margin-left: calc(var(--codice-code-line-number-width) - 2.5rem); ${''}
}
${R} textarea::-webkit-scrollbar,
${R} textarea:focus::-webkit-scrollbar,
${R} textarea:hover::-webkit-scrollbar {
width: 0;
}
${R} [data-codice-content] {
position: relative;
}
${R} textarea {
resize: none;
display: block;
color: var(--codice-text-color);
background-color: var(--codice-background-color);
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
overflow: hidden;
}
${R}[data-codice-line-numbers="true"] textarea {
padding-left: calc(var(--codice-code-line-number-width) + 2px);
}
${R}[data-codice-line-numbers="false"] textarea {
padding-left: var(--codice-code-padding);
}
`;
// line number padding-left is [[width 24px] margin-right 16px] + 15px
const css = ({ fontSize, lineNumbersWidth = '2.5rem', padding = '1rem', fontFamily = 'Consolas, Monaco, monospace' })=>{
return `\
${EDITOR_CSS}
${R} {
--codice-font-size: ${fontSizeCss(fontSize)};
--codice-code-line-number-width: ${lineNumbersWidth};
--codice-code-padding: ${padding};
--codice-font-family: ${fontFamily};
}`;
};
function composeRefs(...refs) {
return (node)=>{
refs.forEach((ref)=>{
if (typeof ref === 'function') {
if (node) {
ref(node);
}
} else if (ref) {
if (node) {
ref.current = node;
}
}
});
};
}
const Editor = /*#__PURE__*/ forwardRef(function Editor({ title, value = '', controls = true, lineNumbers = true, lineNumbersWidth, extension, padding, onChange = ()=>{}, fontSize, fontFamily, onChangeTitle = ()=>{}, ...props }, ref) {
const [code, setCode] = useState(value);
const textareaRef = useRef(null);
function update(textContent) {
setCode(textContent);
onChange(textContent);
}
useEffect(()=>{
if (value !== code) {
update(value);
}
}, [
value,
code
]);
function onInput(event) {
const textContent = event.target.value || '';
update(textContent);
}
return /*#__PURE__*/ jsxs("div", {
...props,
"data-codice": "editor",
"data-codice-editor": true,
// DOM attributes for selecting the stateful editor easily.
// e.g. [data-codice-line-numbers="true"]
"data-codice-title": title || '',
"data-codice-controls": !!controls,
"data-codice-line-numbers": !!lineNumbers,
children: [
/*#__PURE__*/ jsx(ScopedStyle, {
css: css({
fontSize,
padding,
lineNumbersWidth,
fontFamily
})
}),
/*#__PURE__*/ jsx(CodeHeader, {
title: title,
controls: controls,
onChangeTitle: onChangeTitle
}),
/*#__PURE__*/ jsxs("div", {
"data-codice-content": true,
children: [
/*#__PURE__*/ jsx(Code, {
title: null,
extension: extension || getExtension(title),
controls: false,
lineNumbers: lineNumbers,
lineNumbersWidth: lineNumbersWidth,
padding: padding,
children: code
}),
/*#__PURE__*/ jsx("textarea", {
ref: composeRefs(ref, textareaRef),
value: code,
onChange: onInput
})
]
})
]
});
});
export { Editor as E };