overtype 2.3.6 → 2.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overtype",
3
- "version": "2.3.6",
3
+ "version": "2.3.8",
4
4
  "description": "A lightweight markdown editor library with perfect WYSIWYG alignment using an invisible textarea overlay",
5
5
  "main": "dist/overtype.cjs",
6
6
  "module": "dist/overtype.esm.js",
package/src/overtype.d.ts CHANGED
@@ -140,6 +140,8 @@ export interface Options {
140
140
  // Callbacks
141
141
  onChange?: (value: string, instance: OverTypeInstance) => void;
142
142
  onKeydown?: (event: KeyboardEvent, instance: OverTypeInstance) => void;
143
+ onFocus?: (event: FocusEvent, instance: OverTypeInstance) => void;
144
+ onBlur?: (event: FocusEvent, instance: OverTypeInstance) => void;
143
145
  }
144
146
 
145
147
  // Interface for constructor that returns array
@@ -158,7 +160,8 @@ export interface OverTypeConstructor {
158
160
  MarkdownParser: any;
159
161
  ShortcutsManager: any;
160
162
  init(target: string | Element | NodeList | Element[], options?: Options): OverTypeInstance[];
161
- getInstance(element: Element): OverTypeInstance | null;
163
+ initFromData(target: string | Element | NodeList | Element[], defaults?: Options): OverTypeInstance[];
164
+ getInstance(target: string | Element | NodeList | Element[]): OverTypeInstance | null;
162
165
  destroyAll(): void;
163
166
  injectStyles(force?: boolean): void;
164
167
  setTheme(theme: string | Theme, customColors?: Partial<Theme['colors']>): void;
@@ -225,6 +228,26 @@ export type OverType = OverTypeInstance;
225
228
  // Module exports - default export is the constructor
226
229
  export default OverType;
227
230
 
231
+ /** Re-exported markdown-actions. Useful for custom toolbar implementations */
232
+ export const markdownActions: {
233
+ toggleBold(textarea: HTMLTextAreaElement): void;
234
+ toggleItalic(textarea: HTMLTextAreaElement): void;
235
+ toggleCode(textarea: HTMLTextAreaElement): void;
236
+ insertLink(textarea: HTMLTextAreaElement, options?: { url?: string; text?: string }): void;
237
+ toggleBulletList(textarea: HTMLTextAreaElement): void;
238
+ toggleNumberedList(textarea: HTMLTextAreaElement): void;
239
+ toggleQuote(textarea: HTMLTextAreaElement): void;
240
+ toggleTaskList(textarea: HTMLTextAreaElement): void;
241
+ insertHeader(textarea: HTMLTextAreaElement, level?: number, toggle?: boolean): void;
242
+ toggleH1(textarea: HTMLTextAreaElement): void;
243
+ toggleH2(textarea: HTMLTextAreaElement): void;
244
+ toggleH3(textarea: HTMLTextAreaElement): void;
245
+ getActiveFormats(textarea: HTMLTextAreaElement): string[];
246
+ hasFormat(textarea: HTMLTextAreaElement, format: string): boolean;
247
+ expandSelection(textarea: HTMLTextAreaElement, options?: object): void;
248
+ applyCustomFormat(textarea: HTMLTextAreaElement, format: object): void;
249
+ };
250
+
228
251
  /**
229
252
  * Pre-defined toolbar buttons
230
253
  */
package/src/overtype.js CHANGED
@@ -93,23 +93,9 @@ class OverType {
93
93
  * @returns {Array} Array of OverType instances
94
94
  */
95
95
  constructor(target, options = {}) {
96
- // Convert target to array of elements
97
- let elements;
98
-
99
- if (typeof target === 'string') {
100
- elements = document.querySelectorAll(target);
101
- if (elements.length === 0) {
102
- throw new Error(`No elements found for selector: ${target}`);
103
- }
104
- elements = Array.from(elements);
105
- } else if (target instanceof Element) {
106
- elements = [target];
107
- } else if (target instanceof NodeList) {
108
- elements = Array.from(target);
109
- } else if (Array.isArray(target)) {
110
- elements = target;
111
- } else {
112
- throw new Error('Invalid target: must be selector string, Element, NodeList, or Array');
96
+ const elements = OverType._resolveTargets(target);
97
+ if (typeof target === 'string' && elements.length === 0) {
98
+ throw new Error(`No elements found for selector: ${target}`);
113
99
  }
114
100
 
115
101
  // Initialize all elements and return array
@@ -189,7 +175,7 @@ class OverType {
189
175
 
190
176
  // Call onChange if provided
191
177
  if (this.options.onChange) {
192
- this.options.onChange(this.getValue(), this);
178
+ this._notifyChange();
193
179
  }
194
180
  }
195
181
 
@@ -228,6 +214,8 @@ class OverType {
228
214
  onChange: null,
229
215
  onKeydown: null,
230
216
  onRender: null,
217
+ onFocus: null,
218
+ onBlur: null,
231
219
 
232
220
  // Features
233
221
  showActiveLineRaw: false,
@@ -747,17 +735,21 @@ class OverType {
747
735
  this._updateStats();
748
736
  }
749
737
 
750
- // Trigger onChange callback
751
- if (this.options.onChange && this.initialized) {
752
- this.options.onChange(text, this);
753
- }
754
-
755
738
  // Trigger onRender callback
756
739
  if (this.options.onRender) {
757
740
  this.options.onRender(this.preview, isPreviewMode ? 'preview' : 'normal', this);
758
741
  }
759
742
  }
760
743
 
744
+ /**
745
+ * Notify listeners that the editor value changed
746
+ * @private
747
+ */
748
+ _notifyChange() {
749
+ if (!this.options.onChange || !this.initialized) return;
750
+ this.options.onChange(this.textarea.value, this);
751
+ }
752
+
761
753
  /**
762
754
  * Apply background styling to code blocks
763
755
  * @private
@@ -806,6 +798,27 @@ class OverType {
806
798
  */
807
799
  handleInput(event) {
808
800
  this.updatePreview();
801
+ this._notifyChange();
802
+ }
803
+
804
+ /**
805
+ * Handle focus events
806
+ * @private
807
+ */
808
+ handleFocus(event) {
809
+ if (this.options.onFocus) {
810
+ this.options.onFocus(event, this);
811
+ }
812
+ }
813
+
814
+ /**
815
+ * Handle blur events
816
+ * @private
817
+ */
818
+ handleBlur(event) {
819
+ if (this.options.onBlur) {
820
+ this.options.onBlur(event, this);
821
+ }
809
822
  }
810
823
 
811
824
  /**
@@ -1059,6 +1072,7 @@ class OverType {
1059
1072
  * @param {string} value - Markdown content to set
1060
1073
  */
1061
1074
  setValue(value) {
1075
+ const didChange = this.textarea.value !== value;
1062
1076
  this.textarea.value = value;
1063
1077
  this.updatePreview();
1064
1078
 
@@ -1066,6 +1080,10 @@ class OverType {
1066
1080
  if (this.options.autoResize) {
1067
1081
  this._updateAutoHeight();
1068
1082
  }
1083
+
1084
+ if (didChange) {
1085
+ this._notifyChange();
1086
+ }
1069
1087
  }
1070
1088
 
1071
1089
  /**
@@ -1522,9 +1540,9 @@ class OverType {
1522
1540
  * // HTML: <div class="editor" data-ot-toolbar="true" data-ot-theme="cave"></div>
1523
1541
  * OverType.initFromData('.editor', { fontSize: '14px' });
1524
1542
  */
1525
- static initFromData(selector, defaults = {}) {
1526
- const elements = document.querySelectorAll(selector);
1527
- return Array.from(elements).map(el => {
1543
+ static initFromData(target, defaults = {}) {
1544
+ const elements = OverType._resolveTargets(target);
1545
+ return elements.map(el => {
1528
1546
  const options = { ...defaults };
1529
1547
 
1530
1548
  // Parse data-ot-* attributes (kebab-case to camelCase)
@@ -1540,6 +1558,35 @@ class OverType {
1540
1558
  });
1541
1559
  }
1542
1560
 
1561
+ /**
1562
+ * Normalize various target shapes to an array of Elements
1563
+ * @private
1564
+ * @param {string|Element|NodeList|Element[]} target
1565
+ * @returns {Element[]}
1566
+ */
1567
+ static _resolveTargets(target) {
1568
+ if (target == null) {
1569
+ throw new Error('Invalid target: must be selector string, Element, NodeList, or Array');
1570
+ }
1571
+ if (typeof target === 'string') {
1572
+ return Array.from(document.querySelectorAll(target));
1573
+ }
1574
+ if (target instanceof Element) {
1575
+ return [target];
1576
+ }
1577
+ if (target instanceof NodeList) {
1578
+ return Array.from(target);
1579
+ }
1580
+ if (Array.isArray(target)) {
1581
+ return target;
1582
+ }
1583
+ // Array-like (e.g. jQuery objects expose numeric indices and length)
1584
+ if (typeof target.length === 'number') {
1585
+ return Array.from(target);
1586
+ }
1587
+ throw new Error('Invalid target: must be selector string, Element, NodeList, or Array');
1588
+ }
1589
+
1543
1590
  /**
1544
1591
  * Parse a data attribute value to the appropriate type
1545
1592
  * @private
@@ -1553,11 +1600,21 @@ class OverType {
1553
1600
  }
1554
1601
 
1555
1602
  /**
1556
- * Get instance from element
1557
- * @param {Element} element - DOM element
1558
- * @returns {OverType|null} OverType instance or null
1603
+ * Get instance from a target. Accepts the same shapes as the constructor;
1604
+ * for multi-element targets, returns the instance for the first matching
1605
+ * element, or null if none.
1606
+ * @param {string|Element|NodeList|Element[]} target
1607
+ * @returns {OverType|null}
1559
1608
  */
1560
- static getInstance(element) {
1609
+ static getInstance(target) {
1610
+ let element;
1611
+ if (target instanceof Element) {
1612
+ element = target;
1613
+ } else {
1614
+ const elements = OverType._resolveTargets(target);
1615
+ element = elements[0];
1616
+ }
1617
+ if (!element) return null;
1561
1618
  return element.overTypeInstance || OverType.instances.get(element) || null;
1562
1619
  }
1563
1620
 
@@ -1767,6 +1824,24 @@ class OverType {
1767
1824
  }
1768
1825
  });
1769
1826
 
1827
+ // Focus event (capture: true because focus does not bubble)
1828
+ document.addEventListener('focus', (e) => {
1829
+ if (e.target && e.target.classList && e.target.classList.contains('overtype-input')) {
1830
+ const wrapper = e.target.closest('.overtype-wrapper');
1831
+ const instance = wrapper?._instance;
1832
+ if (instance) instance.handleFocus(e);
1833
+ }
1834
+ }, true);
1835
+
1836
+ // Blur event (capture: true because blur does not bubble)
1837
+ document.addEventListener('blur', (e) => {
1838
+ if (e.target && e.target.classList && e.target.classList.contains('overtype-input')) {
1839
+ const wrapper = e.target.closest('.overtype-wrapper');
1840
+ const instance = wrapper?._instance;
1841
+ if (instance) instance.handleBlur(e);
1842
+ }
1843
+ }, true);
1844
+
1770
1845
  // Scroll event
1771
1846
  document.addEventListener('scroll', (e) => {
1772
1847
  if (e.target && e.target.classList && e.target.classList.contains('overtype-input')) {
@@ -1817,3 +1892,7 @@ export { OverType };
1817
1892
 
1818
1893
  // Export toolbar buttons for custom toolbar configurations
1819
1894
  export { toolbarButtons, defaultToolbarButtons } from './toolbar-buttons.js';
1895
+
1896
+ // Re-export markdown-actions. Useful for custom toolbar implementations
1897
+ export * as markdownActions from 'markdown-actions';
1898
+
package/src/parser.js CHANGED
@@ -134,15 +134,15 @@ export class MarkdownParser {
134
134
  * @returns {string} Parsed task list item
135
135
  */
136
136
  static parseTaskList(html, isPreviewMode = false) {
137
- return html.replace(/^((?:&nbsp;)*)-\s+\[([ xX])\]\s+(.+)$/, (match, indent, checked, content) => {
137
+ return html.replace(/^((?:&nbsp;)*)-(\s+)\[([ xX])\](\s*)(.*)$/, (match, indent, spacingBeforeBox, checked, spacingAfterBox, content) => {
138
138
  content = this.parseInlineElements(content);
139
139
  if (isPreviewMode) {
140
140
  // Preview mode: render actual checkbox
141
141
  const isChecked = checked.toLowerCase() === 'x';
142
142
  return `${indent}<li class="task-list"><input type="checkbox" ${isChecked ? 'checked' : ''}> ${content}</li>`;
143
143
  } else {
144
- // Normal mode: keep syntax visible for alignment
145
- return `${indent}<li class="task-list"><span class="syntax-marker">- [${checked}] </span>${content}</li>`;
144
+ // Normal mode: keep syntax (including user spacing) visible for alignment
145
+ return `${indent}<li class="task-list"><span class="syntax-marker">-${spacingBeforeBox}[${checked}]${spacingAfterBox}</span>${content}</li>`;
146
146
  }
147
147
  });
148
148
  }