import { StateEffect, StateField } from '@codemirror/state' import { EditorView, showPanel } from '@codemirror/view' const toggleToolbarEffect = StateEffect.define() const toolbarState = StateField.define({ create: () => true, update: (value, tr) => { for (const effect of tr.effects) { if (effect.is(toggleToolbarEffect)) { value = effect.value } } return value }, provide: f => showPanel.from(f, on => (on ? createToolbarPanel : null)), }) export function createToolbarPanel() { const dom = document.createElement('div') dom.classList.add('ol-cm-toolbar-portal') dom.id = 'ol-cm-toolbar-portal' return { dom, top: true } } const toolbarTheme = EditorView.theme({ '.ol-cm-toolbar-wrapper': { backgroundColor: 'var(--editor-toolbar-bg)', color: 'var(--toolbar-btn-color)', }, '.ol-cm-toolbar': { flex: 1, display: 'flex', overflowX: 'hidden', }, '&.overall-theme-dark .ol-cm-toolbar': { '& img': { filter: 'invert(1)', }, }, '.ol-cm-toolbar-overflow': { display: 'flex', flexWrap: 'wrap', }, '#popover-toolbar-overflow': { padding: 0, border: '1px solid var(--editor-toolbar-popover-border-color)', backgroundColor: 'var(--editor-toolbar-bg)', color: 'var(--toolbar-btn-color)', '& .popover-content, & .popover-body': { padding: 0, }, '& .popover-body': { color: 'inherit', }, '& .arrow, & .popover-arrow': { display: 'none', }, }, '.ol-cm-toolbar-header': { color: 'var(--toolbar-btn-color)', }, '.ol-cm-toolbar-dropdown-divider': { borderBottom: '1px solid', borderColor: 'var(--toolbar-dropdown-divider-color)', }, // here render both the icons, and hide one depending on if its dark or light mode with &.overall-theme-dark '.ol-cm-toolbar-ai-sparkle-gradient': { display: 'block', }, '.ol-cm-toolbar-ai-sparkle-white': { display: 'none', }, '&.overall-theme-dark .ol-cm-toolbar-ai-sparkle-gradient': { display: 'none', }, '&.overall-theme-dark .ol-cm-toolbar-ai-sparkle-white': { display: 'block', }, '.ol-cm-toolbar-button-menu-popover': { backgroundColor: 'initial', border: '1px solid var(--editor-toolbar-popover-border-color)', '& > .popover-content, & > .popover-body': { padding: 0, color: 'initial', }, '& .arrow, & .popover-arrow': { display: 'none', }, '& .list-group': { marginBottom: 0, backgroundColor: 'var(--editor-toolbar-bg)', borderRadius: '4px', }, '& .list-group-item': { width: '100%', textAlign: 'start', display: 'flex', alignItems: 'center', gap: '5px', color: 'var(--toolbar-btn-color)', borderColor: 'var(--editor-toolbar-bg)', background: 'none', '&.active': { backgroundColor: 'rgba(125, 125, 125, 0.1)', }, '&:hover, &:focus': { backgroundColor: 'rgba(125, 125, 125, 0.2)', }, '&.ol-cm-section-heading-menu-item': { border: 'none', padding: '4px 12px', height: '40px', fontSize: '14px', fontWeight: 'bold', }, '&.section-level-section': { fontSize: '1.44em', }, '&.section-level-subsection': { fontSize: '1.2em', }, '&.section-level-text': { fontWeight: 'normal', }, }, }, '.ol-cm-toolbar-button-menu-popover-horizontal': { '& .list-group': { flexDirection: 'row', }, }, '.ol-cm-toolbar-button-group': { display: 'flex', alignItems: 'center', whiteSpace: 'nowrap', flexWrap: 'nowrap', padding: '0 4px', margin: '4px 0', lineHeight: '1', '&:not(:first-child)': { borderLeft: '1px solid rgba(125, 125, 125, 0.3)', }, '&:has(> [data-overflow]):not(:has(> [data-overflow]:not(.overflow-hidden)))': { borderLeft: 'none', padding: 0, }, '&:empty': { display: 'none', }, '&.ol-cm-toolbar-end': { borderLeft: 'none', }, '&.ol-cm-toolbar-stretch': { flex: 1, }, '&.overflow-hidden': { borderLeft: 'none', width: 0, padding: 0, }, '& > [data-overflow].overflow-hidden': { width: 0, overflow: 'hidden', }, }, '.ol-cm-toolbar-wrapper-needs-border .ol-cm-toolbar-button-group:first-child': { borderLeft: '1px solid rgba(125, 125, 125, 0.3)', }, '.ol-cm-toolbar-button': { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0', margin: '0 1px', backgroundColor: 'transparent', border: 'none', borderRadius: 'var(--border-radius-base)', lineHeight: '1', width: '24px', height: '24px', overflow: 'hidden', color: 'inherit', '&:hover, &:focus, &:active, &.active': { backgroundColor: 'rgba(125, 125, 125, 0.1)', color: 'inherit', boxShadow: 'none', '&[aria-disabled="true"]': { opacity: '0.2', }, }, '&.active, &:active': { backgroundColor: 'rgba(125, 125, 125, 0.2)', }, '&[aria-disabled="true"]': { opacity: '0.2', cursor: 'not-allowed', }, '.overflow-hidden &': { display: 'none', }, '&.ol-cm-toolbar-button-math': { fontFamily: '"Noto Serif", serif', fontSize: '16px', fontWeight: 700, }, }, '.ol-cm-toolbar-button.ol-cm-toolbar-button-wide': { width: 'auto', }, '&.overall-theme-dark .ol-cm-toolbar-button': { opacity: 0.8, '&:hover, &:focus, &:active, &.active': { backgroundColor: 'rgba(125, 125, 125, 0.2)', }, '&.active, &:active': { backgroundColor: 'rgba(125, 125, 125, 0.4)', }, '&[aria-disabled="true"]': { opacity: 0.2, }, }, '.ol-cm-toolbar-end': { justifyContent: 'flex-end', '& .badge': { marginRight: '5px', }, }, '.ol-cm-toolbar-overflow-toggle': { display: 'none', '&.ol-cm-toolbar-overflow-toggle-visible': { display: 'flex', }, }, '&.overall-theme-dark .ol-cm-toolbar-table-grid': { '& td.active': { outlineColor: 'white', background: 'rgb(125, 125, 125)', }, }, '.ol-cm-toolbar-table-grid': { borderCollapse: 'separate', tableLayout: 'fixed', fontSize: '6px', cursor: 'pointer', width: '160px', '& td': { outline: '1px solid #E7E9EE', outlineOffset: '-2px', width: '16px', height: '16px', '&.active': { outlineColor: '#3265B2', background: '#F1F4F9', }, }, }, '.ol-cm-toolbar-table-size-label': { maxWidth: '160px', fontFamily: 'Lato, sans-serif', fontSize: '12px', }, '.ol-cm-toolbar-table-grid-popover': { maxWidth: 'unset', padding: '8px', boxShadow: '0 5px 10px rgba(0, 0, 0, 0.2)', borderRadius: '4px', backgroundColor: 'var(--editor-toolbar-bg)', pointerEvents: 'all', color: 'var(--toolbar-btn-color)', }, '.ol-cm-toolbar-button-menu-popover-unstyled': { maxWidth: 'unset', background: 'transparent', border: 0, padding: '0 8px 8px 160px', boxShadow: 'none', pointerEvents: 'none', }, }) const toolbarBorderTheme = EditorView.baseTheme({ '&.overall-theme-dark .cm-panels-top': { borderBottom: '1px solid var(--border-divider-dark)', }, }) /** * A panel which contains the editor toolbar, provided by a state field which allows the toolbar to be toggled, * and styles for the toolbar. */ export const toolbarPanel = () => [ toolbarState, toolbarTheme, toolbarBorderTheme, ]