UNPKG

vzcode

Version:
175 lines (153 loc) 4.6 kB
import { Extension, RangeSet } from '@codemirror/state'; import { Decoration, EditorView, ViewPlugin, WidgetType, } from '@codemirror/view'; import { colorPickerRegex } from './colorPicker'; const colorCircleTheme = EditorView.baseTheme({ '.color-circle-parent': { display: 'inline-block', cursor: 'inherit', }, }); class ColorWidget extends WidgetType { color: string; constructor(color: string) { super(); this.color = color; } eq(widget: ColorWidget): boolean { return widget.color === this.color; } toDOM(): HTMLElement { const parent = document.createElement('div'); const size = 20; const innerSize = 14; parent.setAttribute( 'style', `width:${size}px;height:${size}px;position:relative;`, // Position relative for the hovering tool tip to work ); parent.className = 'color-circle-parent'; const svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg', ); const colorCircle = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle', ); colorCircle.setAttributeNS( null, 'fill', this.color.replace(/["']/g, ''), ); colorCircle.setAttributeNS(null, 'stroke', 'white'); colorCircle.setAttributeNS( null, 'stroke-width', '0.75', ); colorCircle.setAttributeNS( null, 'r', '' + innerSize / 2, ); colorCircle.setAttributeNS(null, 'cx', '' + size / 2); colorCircle.setAttributeNS( null, 'cy', '' + (size / 2 - 1), ); svg.setAttributeNS(null, 'width', '' + size); svg.setAttributeNS(null, 'height', '' + size); svg.appendChild(colorCircle); parent.appendChild(svg); // Hovering tool tip shortcut for when users hover over color circle const hoveringTooltip = document.createElement('div'); // Set class for tooltip to use CSS styles hoveringTooltip.className = 'tooltip_hover_circle'; hoveringTooltip.innerHTML = `<strong>Alt + Click on a hex color</strong><br /> <div>Open a color picker to modify the color</div>`; parent.appendChild(hoveringTooltip); // Timer to prevent constant pop-ups when scrolling through code let hoverTimeout: number; // Fade in and fade out effect for when user hovers over the circle // Mouse Event to set y and x of the tool tip pop up to prevent overlap onto circle parent.addEventListener( 'mouseenter', (event: MouseEvent) => { hoverTimeout = window.setTimeout(() => { hoveringTooltip.style.top = `${event.clientY - 60}px`; hoveringTooltip.style.left = `${event.clientX}px`; hoveringTooltip.style.opacity = '1'; }, 700); }, ); parent.addEventListener('mouseleave', () => { clearTimeout(hoverTimeout); hoveringTooltip.style.opacity = '0'; }); return parent; } ignoreEvent() { return false; } } export const colorsInTextPlugin: Extension = [ ViewPlugin.fromClass( class { decorations: any; view: EditorView; constructor(view: EditorView) { this.decorations = RangeSet.of([]); this.view = view; } }, { decorations: (v) => { const colorInfos = []; const lines = v.view.state.doc.iter(); let line = lines.next(); // Offset is the number of characters before the hex // so the circle can be placed properly. let offset = 0; while (!line.done) { if (line.value === '\n') { offset++; line = lines.next(); continue; } const hexColorOccurences = line.value.matchAll( colorPickerRegex, ); let hexOccurance = hexColorOccurences.next(); while (!hexOccurance.done) { const offsetColorInfo = hexOccurance.value; offsetColorInfo.index += offset; colorInfos.push(offsetColorInfo); hexOccurance = hexColorOccurences.next(); } offset += line.value.length; line = lines.next(); } return Decoration.set( colorInfos.map((colorInfo) => { return { // 7 is the length of the hex color string from: colorInfo.index + 7, to: colorInfo.index + 7, value: Decoration.widget({ side: -1, widget: new ColorWidget(colorInfo[0]), }), }; }), ); }, }, ), colorCircleTheme, ];