@livingdocs/editable.js 5.1.0 → 5.1.2

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@livingdocs/editable.js",
3
3
  "description": "Friendly contenteditable API",
4
- "version": "5.1.0",
4
+ "version": "5.1.2",
5
5
  "dependencies": {
6
6
  "@babel/runtime": "^7.16.7",
7
7
  "lodash-es": "^4.17.21"
@@ -13,14 +13,14 @@
13
13
  "@babel/preset-env": "^7.16.11",
14
14
  "@babel/preset-react": "^7.16.7",
15
15
  "@eslint/compat": "^1.1.0",
16
- "babel-loader": "^9.0.0",
16
+ "babel-loader": "^10.0.0",
17
17
  "babel-plugin-add-module-exports": "^1.0.4",
18
18
  "babel-plugin-istanbul": "^7.0.0",
19
19
  "chai": "^5.0.0",
20
20
  "css-loader": "^7.0.0",
21
21
  "eslint": "^9.6.0",
22
22
  "eslint-plugin-react": "^7.34.3",
23
- "globals": "^15.8.0",
23
+ "globals": "^16.0.0",
24
24
  "karma": "^6.3.13",
25
25
  "karma-chrome-launcher": "^3.1.0",
26
26
  "karma-coverage": "^2.1.0",
@@ -82,11 +82,11 @@
82
82
  "isCi": true,
83
83
  "name": "Drone",
84
84
  "service": "drone",
85
- "commit": "7e5149c84554e50fe42304df6a3e2b94e5bc63f7",
86
- "build": "1965",
85
+ "commit": "353179e4f72107cca78fac6d232921a4c42f9815",
86
+ "build": "2012",
87
87
  "branch": "master",
88
88
  "isPr": false,
89
89
  "slug": "livingdocsIO/editable.js",
90
- "date": "2025-02-20T14:00:02.426Z"
90
+ "date": "2025-03-20T07:08:54.260Z"
91
91
  }
92
92
  }
package/src/dispatcher.js CHANGED
@@ -153,7 +153,7 @@ export default class Dispatcher {
153
153
  currentInput.offset = selection.range?.startOffset
154
154
  setTimeout(() => {
155
155
  applySmartQuotes(selection.range, config, evt.data, evt.target, currentInput.offset)
156
- }, 500
156
+ }, 300
157
157
  )
158
158
  }
159
159
 
@@ -5,24 +5,27 @@ export const shouldApplySmartQuotes = (config, target) => {
5
5
  return !!smartQuotes && isValidQuotePairConfig(quotes) && isValidQuotePairConfig(singleQuotes) && target.isContentEditable
6
6
  }
7
7
 
8
- const isDoubleQuote = (char) => /^[«»"“”„]$/.test(char)
9
- const isSingleQuote = (char) => /^[‘’‹›‚']$/.test(char)
10
- const isApostrophe = (char) => /^[’']$/.test(char)
11
- const isWhitespace = (char) => /^\s$/.test(char)
12
- const isSeparatorOrWhitespace = (char) => /\s|[>\-–—]/.test(char)
8
+ export const isDoubleQuote = (char) => /^[«»"“”„]$/.test(char)
9
+ export const isSingleQuote = (char) => /^[‘’‹›‚']$/.test(char)
10
+ export const isApostrophe = (char) => /^[’']$/.test(char)
11
+ export const isWhitespace = (char) => /^\s$/.test(char)
12
+ export const isSeparatorOrWhitespace = (char) => /\s|[>\-–—]/.test(char)
13
13
 
14
14
  const shouldBeOpeningQuote = (text, indexCharBefore) => indexCharBefore < 0 || isSeparatorOrWhitespace(text[indexCharBefore])
15
15
  const shouldBeClosingQuote = (text, indexCharBefore) => !!text[indexCharBefore] && !isSeparatorOrWhitespace(text[indexCharBefore])
16
16
  const hasCharAfter = (textArr, indexCharAfter) => !!textArr[indexCharAfter] && !isWhitespace(textArr[indexCharAfter])
17
+ const shouldBeSingleOpeningQuote = (text, indexCharBefore) => !!text[indexCharBefore] && isDoubleQuote(text[indexCharBefore])
17
18
 
18
- const replaceQuote = (range, index, quoteType) => {
19
- const startContainer = range.startContainer
20
- if (!startContainer.nodeValue) {
21
- return
19
+ export const replaceQuote = (range, index, quoteType) => {
20
+ const startContainer = range?.startContainer
21
+ const nodeValue = startContainer?.nodeValue
22
+ if (!nodeValue) {
23
+ return null
22
24
  }
23
- const textNode = document.createTextNode(`${startContainer.nodeValue.substring(0, index)}${quoteType}${startContainer.nodeValue.substring(index + 1)}`)
24
- startContainer.replaceWith(textNode)
25
- return textNode
25
+ const newText = `${nodeValue.substring(0, index)}${quoteType}${nodeValue.substring(index + 1)}`
26
+ const newTextNode = document.createTextNode(newText)
27
+ startContainer.replaceWith(newTextNode)
28
+ return newTextNode
26
29
  }
27
30
 
28
31
  const hasSingleOpeningQuote = (textArr, offset, singleOpeningQuote) => {
@@ -54,7 +57,11 @@ export const applySmartQuotes = (range, config, char, target, cursorOffset) => {
54
57
  const textArr = [...range.startContainer.textContent]
55
58
  let newTextNode
56
59
 
57
- if (shouldBeClosingQuote(textArr, offset - 2)) {
60
+ // Special case for a single quote following a double quote,
61
+ // which should be transformed into a single opening quote
62
+ if (isCharSingleQuote && shouldBeSingleOpeningQuote(textArr, offset - 2)) {
63
+ newTextNode = replaceQuote(range, offset - 1, singleQuotes[0])
64
+ } else if (shouldBeClosingQuote(textArr, offset - 2)) {
58
65
  if (isCharSingleQuote) {
59
66
  // Don't transform apostrophes
60
67
  if (hasCharAfter(textArr, offset)) {