diff options
| author | Hsieh Chin Fan <pham@topo.tw> | 2024-10-10 11:51:57 +0800 |
|---|---|---|
| committer | Hsieh Chin Fan <pham@topo.tw> | 2024-10-10 13:18:41 +0800 |
| commit | bfafe34d5c79f30f274c66709775ed8b13a016c5 (patch) | |
| tree | f7d9ef26c65d13d89cb299c30224649201c6c13a | |
| parent | 3d9865d91d46fe398bcd668859370fdbed7d90cd (diff) | |
docs(jsdoc): update jsdoc
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | src/Layout.mjs | 26 | ||||
| -rw-r--r-- | src/MenuItem.mjs | 36 | ||||
| -rw-r--r-- | src/dumbymap.mjs | 18 | ||||
| -rw-r--r-- | src/editor.mjs | 63 |
5 files changed, 104 insertions, 40 deletions
diff --git a/package.json b/package.json index 78a217c..7487e9c 100644 --- a/package.json +++ b/package.json | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | "dev": "npm run server", | 28 | "dev": "npm run server", |
| 29 | "lint": "standard --fix", | 29 | "lint": "standard --fix", |
| 30 | "style": "scripts/stylelint.sh", | 30 | "style": "scripts/stylelint.sh", |
| 31 | "docs": "jsdoc -c scripts/jsdoc.conf; xdg-open http://localhost:8080/docs/", | ||
| 31 | "prepack": "npm run lint && npm run style && npm run build", | 32 | "prepack": "npm run lint && npm run style && npm run build", |
| 32 | "postpack": "rm -rf dist/css dist/renderers; ln -sf `pwd`/src/css dist; cp node_modules/easymde/dist/easymde.min.css src/css; ln -sf `pwd`/mapclay/dist/renderers dist" | 33 | "postpack": "rm -rf dist/css dist/renderers; ln -sf `pwd`/src/css dist; cp node_modules/easymde/dist/easymde.min.css src/css; ln -sf `pwd`/mapclay/dist/renderers dist" |
| 33 | }, | 34 | }, |
diff --git a/src/Layout.mjs b/src/Layout.mjs index 5236475..7b14926 100644 --- a/src/Layout.mjs +++ b/src/Layout.mjs | |||
| @@ -2,13 +2,17 @@ import PlainDraggable from 'plain-draggable' | |||
| 2 | import { onRemove, animateRectTransition } from './utils' | 2 | import { onRemove, animateRectTransition } from './utils' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Layout. Basic class for layout | 5 | * Basic class for layout |
| 6 | */ | 6 | */ |
| 7 | export class Layout { | 7 | export class Layout { |
| 8 | /** | 8 | /** |
| 9 | * constructor. | 9 | * Creates a new Layout instance |
| 10 | * | 10 | * |
| 11 | * @param {} options | 11 | * @param {Object} options - The options for the layout |
| 12 | * @param {string} options.name - The name of the layout | ||
| 13 | * @param {Function} [options.enterHandler] - Handler called when entering the layout | ||
| 14 | * @param {Function} [options.leaveHandler] - Handler called when leaving the layout | ||
| 15 | * @throws {Error} If the layout name is not provided | ||
| 12 | */ | 16 | */ |
| 13 | constructor (options = {}) { | 17 | constructor (options = {}) { |
| 14 | if (!options.name) throw Error('Layout name is not given') | 18 | if (!options.name) throw Error('Layout name is not given') |
| @@ -18,7 +22,9 @@ export class Layout { | |||
| 18 | } | 22 | } |
| 19 | 23 | ||
| 20 | /** | 24 | /** |
| 21 | * valueOf. | 25 | * Returns the name of the layout |
| 26 | * | ||
| 27 | * @returns {string} The name of the layout | ||
| 22 | */ | 28 | */ |
| 23 | valueOf = () => this.name | 29 | valueOf = () => this.name |
| 24 | } | 30 | } |
| @@ -35,9 +41,12 @@ export class SideBySide extends Layout { | |||
| 35 | name = 'side-by-side' | 41 | name = 'side-by-side' |
| 36 | 42 | ||
| 37 | /** | 43 | /** |
| 38 | * enterHandler. | 44 | * Handler called when entering the Side-By-Side layout |
| 39 | * | 45 | * |
| 40 | * @param {} | 46 | * @param {Object} options - The options object |
| 47 | * @param {HTMLElement} options.container - The main container element | ||
| 48 | * @param {HTMLElement} options.htmlHolder - The HTML content holder | ||
| 49 | * @param {HTMLElement} options.showcase - The showcase element | ||
| 41 | */ | 50 | */ |
| 42 | enterHandler = ({ container, htmlHolder, showcase }) => { | 51 | enterHandler = ({ container, htmlHolder, showcase }) => { |
| 43 | const bar = document.createElement('div') | 52 | const bar = document.createElement('div') |
| @@ -71,9 +80,10 @@ export class SideBySide extends Layout { | |||
| 71 | } | 80 | } |
| 72 | 81 | ||
| 73 | /** | 82 | /** |
| 74 | * leaveHandler. | 83 | * Handler called when leaving the Side-By-Side layout |
| 75 | * | 84 | * |
| 76 | * @param {} | 85 | * @param {Object} options - The options object |
| 86 | * @param {HTMLElement} options.container - The main container element | ||
| 77 | */ | 87 | */ |
| 78 | leaveHandler = ({ container }) => { | 88 | leaveHandler = ({ container }) => { |
| 79 | container.querySelector('.bar')?.remove() | 89 | container.querySelector('.bar')?.remove() |
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index 0fea539..8b54539 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
| @@ -1,15 +1,28 @@ | |||
| 1 | import { shiftByWindow } from './utils.mjs' | 1 | import { shiftByWindow } from './utils.mjs' |
| 2 | 2 | ||
| 3 | /** | 3 | /** |
| 4 | * Item. Basic Element for menu item | 4 | * @typedef {Object} RefLink |
| 5 | * @property {string} ref -- name of link | ||
| 6 | * @property {string} link -- content of link | ||
| 7 | * @property {string|null} title -- title of link | ||
| 8 | */ | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Basic Element for menu item | ||
| 5 | * | 12 | * |
| 6 | * @extends {window.HTMLDivElement} | 13 | * @extends {window.HTMLDivElement} |
| 7 | */ | 14 | */ |
| 8 | export class Item extends window.HTMLDivElement { | 15 | export class Item extends window.HTMLDivElement { |
| 9 | /** | 16 | /** |
| 10 | * constructor. | 17 | * Creates a new Item instance |
| 11 | * | 18 | * |
| 12 | * @param {Object} | 19 | * @param {Object} options - The options for the item |
| 20 | * @param {string} [options.text] - The text content of the item | ||
| 21 | * @param {string} [options.innerHTML] - The HTML content of the item | ||
| 22 | * @param {string} [options.title] - The title attribute for the item | ||
| 23 | * @param {Function} [options.onclick] - The click event handler | ||
| 24 | * @param {string} [options.style] - The CSS style string | ||
| 25 | * @param {string[]} [options.className] - Additional CSS classes | ||
| 13 | */ | 26 | */ |
| 14 | constructor ({ text, innerHTML, title, onclick, style, className }) { | 27 | constructor ({ text, innerHTML, title, onclick, style, className }) { |
| 15 | super() | 28 | super() |
| @@ -30,15 +43,18 @@ export class Item extends window.HTMLDivElement { | |||
| 30 | window.customElements.define('menu-item', Item, { extends: 'div' }) | 43 | window.customElements.define('menu-item', Item, { extends: 'div' }) |
| 31 | 44 | ||
| 32 | /** | 45 | /** |
| 33 | * Folder. Basic Element for menu item, it generate submenu on hover | 46 | * Basic Element for menu item that generates a submenu on hover |
| 34 | * | 47 | * |
| 35 | * @extends {window.HTMLDivElement} | 48 | * @extends {window.HTMLDivElement} |
| 36 | */ | 49 | */ |
| 37 | export class Folder extends window.HTMLDivElement { | 50 | export class Folder extends window.HTMLDivElement { |
| 38 | /** | 51 | /** |
| 39 | * constructor. | 52 | * Creates a new Folder instance |
| 40 | * | 53 | * |
| 41 | * @param {} | 54 | * @param {Object} options - The options for the folder |
| 55 | * @param {string} [options.text] - The text content of the folder | ||
| 56 | * @param {string} [options.innerHTML] - The HTML content of the folder | ||
| 57 | * @param {Item[]} options.items - The submenu items | ||
| 42 | */ | 58 | */ |
| 43 | constructor ({ text, innerHTML, items }) { | 59 | constructor ({ text, innerHTML, items }) { |
| 44 | super() | 60 | super() |
| @@ -67,9 +83,11 @@ export class Folder extends window.HTMLDivElement { | |||
| 67 | window.customElements.define('menu-folder', Folder, { extends: 'div' }) | 83 | window.customElements.define('menu-folder', Folder, { extends: 'div' }) |
| 68 | 84 | ||
| 69 | /** | 85 | /** |
| 70 | * pickMapItem. | 86 | * Creates a menu item for picking a map |
| 71 | * | 87 | * |
| 72 | * @param {Function[]} options.utils | 88 | * @param {Object} options - The options object |
| 89 | * @param {Object} options.utils - Utility functions | ||
| 90 | * @returns {Folder} A Folder instance for picking a map | ||
| 73 | */ | 91 | */ |
| 74 | export const pickMapItem = ({ utils }) => | 92 | export const pickMapItem = ({ utils }) => |
| 75 | new Folder({ | 93 | new Folder({ |
| @@ -372,7 +390,7 @@ export const restoreCamera = map => | |||
| 372 | * addRefLink. replace selected text into markdown link by reference style links | 390 | * addRefLink. replace selected text into markdown link by reference style links |
| 373 | * | 391 | * |
| 374 | * @param {CodeMirror} cm | 392 | * @param {CodeMirror} cm |
| 375 | * @param {Object[]} refLinks -- object for { ref, link } | 393 | * @param {RefLink[]} refLinks |
| 376 | */ | 394 | */ |
| 377 | export const addRefLink = (cm, refLinks) => | 395 | export const addRefLink = (cm, refLinks) => |
| 378 | new Folder({ | 396 | new Folder({ |
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index ac8392c..b01c73e 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -26,10 +26,11 @@ const defaultLayouts = [ | |||
| 26 | const mapCache = {} | 26 | const mapCache = {} |
| 27 | 27 | ||
| 28 | /** | 28 | /** |
| 29 | * markdown2HTML. | 29 | * Converts Markdown content to HTML and prepares it for DumbyMap rendering |
| 30 | * | 30 | * |
| 31 | * @param {HTMLElement} container -- Target Element to include generated HTML contents | 31 | * @param {HTMLElement} container - Target Element to include generated HTML contents |
| 32 | * @param {String} mdContent -- Texts in Markdown | 32 | * @param {string} mdContent - Texts in Markdown format |
| 33 | * @returns {Object} An object representing the DumbyMap instance | ||
| 33 | */ | 34 | */ |
| 34 | export const markdown2HTML = (container, mdContent) => { | 35 | export const markdown2HTML = (container, mdContent) => { |
| 35 | /** Prepare Elements for Container */ | 36 | /** Prepare Elements for Container */ |
| @@ -106,11 +107,12 @@ export const markdown2HTML = (container, mdContent) => { | |||
| 106 | } | 107 | } |
| 107 | 108 | ||
| 108 | /** | 109 | /** |
| 109 | * generateMaps. | 110 | * Generates maps based on the provided configuration |
| 110 | * | 111 | * |
| 111 | * @param {HTMLElement} container -- Target Element contains HTML contents | 112 | * @param {HTMLElement} container - The container element for the maps |
| 112 | * @param {Number} options.delay -- delay of map generation, milliseconds | 113 | * @param {Object} options - Configuration options |
| 113 | * @return {Object} dumbymap -- Include and Elements and Methods about managing contents | 114 | * @param {number} [options.delay=1000] - Delay before rendering maps (in milliseconds) |
| 115 | * @param {Function} options.mapCallback - Callback function to be called after map rendering | ||
| 114 | */ | 116 | */ |
| 115 | export const generateMaps = (container, { layouts = [], delay, renderCallback } = {}) => { | 117 | export const generateMaps = (container, { layouts = [], delay, renderCallback } = {}) => { |
| 116 | /** Prepare Contaner/HTML Holder/Showcase */ | 118 | /** Prepare Contaner/HTML Holder/Showcase */ |
| @@ -509,4 +511,4 @@ export const generateMaps = (container, { layouts = [], delay, renderCallback } | |||
| 509 | ) | 511 | ) |
| 510 | 512 | ||
| 511 | return Object.seal(dumbymap) | 513 | return Object.seal(dumbymap) |
| 512 | } | 514 | } \ No newline at end of file |
diff --git a/src/editor.mjs b/src/editor.mjs index 8cb7045..5c65af4 100644 --- a/src/editor.mjs +++ b/src/editor.mjs | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | /* global EasyMDE */ | 1 | /* global EasyMDE */ |
| 2 | /* eslint no-undef: "error" */ | ||
| 3 | import { markdown2HTML, generateMaps } from './dumbymap' | 2 | import { markdown2HTML, generateMaps } from './dumbymap' |
| 4 | import { defaultAliases, parseConfigsFromYaml } from 'mapclay' | 3 | import { defaultAliases, parseConfigsFromYaml } from 'mapclay' |
| 5 | import * as menuItem from './MenuItem' | 4 | import * as menuItem from './MenuItem' |
| @@ -7,18 +6,41 @@ import { addAnchorByPoint } from './dumbyUtils.mjs' | |||
| 7 | import { shiftByWindow } from './utils.mjs' | 6 | import { shiftByWindow } from './utils.mjs' |
| 8 | import LeaderLine from 'leader-line' | 7 | import LeaderLine from 'leader-line' |
| 9 | 8 | ||
| 9 | /** | ||
| 10 | * @typedef {Object} RefLink | ||
| 11 | * @property {string} ref -- name of link | ||
| 12 | * @property {string} link -- content of link | ||
| 13 | * @property {string|null} title -- title of link | ||
| 14 | */ | ||
| 15 | |||
| 10 | // Set up Containers {{{ | 16 | // Set up Containers {{{ |
| 11 | /** Variables about dumbymap and editor **/ | 17 | /** Variables about dumbymap and editor **/ |
| 12 | const url = new URL(window.location) | 18 | const url = new URL(window.location) |
| 13 | const context = document.querySelector('[data-mode]') | 19 | const context = document.querySelector('[data-mode]') |
| 14 | const dumbyContainer = document.querySelector('.DumbyMap') | 20 | const dumbyContainer = document.querySelector('.DumbyMap') |
| 21 | dumbyContainer.dataset.scrollLine = '' | ||
| 15 | const textArea = document.querySelector('.editor textarea') | 22 | const textArea = document.querySelector('.editor textarea') |
| 16 | let dumbymap | 23 | let dumbymap |
| 17 | 24 | ||
| 18 | const refLinkPattern = /\[([^\x5B\x5D]+)\]:\s+(.+)/ | 25 | /** Variables about Reference Style Links in Markdown */ |
| 26 | const refLinkPattern = /\[([^\x5B\x5D]+)\]:\s+(\S+)(\s["'](\S+)["'])?/ | ||
| 19 | let refLinks = [] | 27 | let refLinks = [] |
| 28 | |||
| 29 | /** | ||
| 30 | * Validates if the given anchor name is unique | ||
| 31 | * | ||
| 32 | * @param {string} anchorName - The anchor name to validate | ||
| 33 | * @returns {boolean} True if the anchor name is unique, false otherwise | ||
| 34 | */ | ||
| 20 | const validateAnchorName = anchorName => | 35 | const validateAnchorName = anchorName => |
| 21 | !refLinks.find(obj => obj.ref === anchorName) | 36 | !refLinks.find(obj => obj.ref === anchorName) |
| 37 | |||
| 38 | /** | ||
| 39 | * Appends a reference link to the CodeMirror instance | ||
| 40 | * | ||
| 41 | * @param {CodeMirror} cm - The CodeMirror instance | ||
| 42 | * @param {RefLink} refLink - The reference link to append | ||
| 43 | */ | ||
| 22 | const appendRefLink = (cm, refLink) => { | 44 | const appendRefLink = (cm, refLink) => { |
| 23 | const { ref, link, title } = refLink | 45 | const { ref, link, title } = refLink |
| 24 | let refLinkString = `\n[${ref}]: ${link} "${title ?? ''}"` | 46 | let refLinkString = `\n[${ref}]: ${link} "${title ?? ''}"` |
| @@ -46,7 +68,7 @@ new window.MutationObserver(() => { | |||
| 46 | attributeOldValue: true, | 68 | attributeOldValue: true, |
| 47 | }) | 69 | }) |
| 48 | /** | 70 | /** |
| 49 | * toggleEditing: toggle editing mode | 71 | * Toggles the editing mode |
| 50 | */ | 72 | */ |
| 51 | const toggleEditing = () => { | 73 | const toggleEditing = () => { |
| 52 | const mode = context.dataset.mode | 74 | const mode = context.dataset.mode |
| @@ -236,12 +258,15 @@ const editor = new EasyMDE({ | |||
| 236 | /** CodeMirror Instance **/ | 258 | /** CodeMirror Instance **/ |
| 237 | const cm = editor.codemirror | 259 | const cm = editor.codemirror |
| 238 | 260 | ||
| 239 | /** Ref Links **/ | 261 | /** |
| 262 | * getRefLinks from contents of editor | ||
| 263 | * @return {RefLink[]} refLinks | ||
| 264 | */ | ||
| 240 | const getRefLinks = () => editor.value() | 265 | const getRefLinks = () => editor.value() |
| 241 | .split('\n') | 266 | .split('\n') |
| 242 | .map(line => { | 267 | .map(line => { |
| 243 | const [, ref, link] = line.match(refLinkPattern) ?? [] | 268 | const [, ref, link,, title] = line.match(refLinkPattern) ?? [] |
| 244 | return { ref, link } | 269 | return { ref, link, title } |
| 245 | }) | 270 | }) |
| 246 | .filter(({ ref, link }) => ref && link) | 271 | .filter(({ ref, link }) => ref && link) |
| 247 | 272 | ||
| @@ -284,8 +309,12 @@ if (url.searchParams.get('content') === 'tutorial') { | |||
| 284 | // }}} | 309 | // }}} |
| 285 | // Set up logic about editor content {{{ | 310 | // Set up logic about editor content {{{ |
| 286 | 311 | ||
| 287 | /** Sync scroll from HTML to CodeMirror **/ | 312 | /** |
| 288 | const htmlOnScroll = (ele) => () => { | 313 | * updateScrollLine. Update data attribute by scroll on given element |
| 314 | * | ||
| 315 | * @param {HTMLElement} ele | ||
| 316 | */ | ||
| 317 | const updateScrollLine = (ele) => () => { | ||
| 289 | if (textArea.dataset.scrollLine) return | 318 | if (textArea.dataset.scrollLine) return |
| 290 | 319 | ||
| 291 | const threshold = ele.scrollTop + window.innerHeight / 2 + 30 | 320 | const threshold = ele.scrollTop + window.innerHeight / 2 + 30 |
| @@ -300,14 +329,14 @@ const htmlOnScroll = (ele) => () => { | |||
| 300 | const offset = (line.offsetTop + block.offsetTop - ele.scrollTop) | 329 | const offset = (line.offsetTop + block.offsetTop - ele.scrollTop) |
| 301 | 330 | ||
| 302 | if (linenumber) { | 331 | if (linenumber) { |
| 303 | dumbyContainer.dataset.scrollLine = linenumber + '/' + offset | 332 | ele.closest('[data-scroll-line]').dataset.scrollLine = linenumber + '/' + offset |
| 304 | } | 333 | } |
| 305 | } | 334 | } |
| 306 | 335 | ||
| 307 | new window.MutationObserver(() => { | 336 | new window.MutationObserver(() => { |
| 308 | clearTimeout(dumbyContainer.timer) | 337 | clearTimeout(dumbyContainer.timer) |
| 309 | dumbyContainer.timer = setTimeout( | 338 | dumbyContainer.timer = setTimeout( |
| 310 | () => delete dumbyContainer.dataset.scrollLine, | 339 | () => { dumbyContainer.dataset.scrollLine = '' }, |
| 311 | 50, | 340 | 50, |
| 312 | ) | 341 | ) |
| 313 | 342 | ||
| @@ -324,7 +353,11 @@ new window.MutationObserver(() => { | |||
| 324 | attributeFilter: ['data-scroll-line'], | 353 | attributeFilter: ['data-scroll-line'], |
| 325 | }) | 354 | }) |
| 326 | 355 | ||
| 327 | const setScrollLine = () => { | 356 | /** |
| 357 | * updateScrollLineByCodeMirror. | ||
| 358 | * @param {CodeMirror} cm | ||
| 359 | */ | ||
| 360 | const updateCMScrollLine = (cm) => { | ||
| 328 | if (dumbyContainer.dataset.scrollLine) return | 361 | if (dumbyContainer.dataset.scrollLine) return |
| 329 | 362 | ||
| 330 | const lineNumber = cm.getCursor()?.line ?? | 363 | const lineNumber = cm.getCursor()?.line ?? |
| @@ -332,14 +365,14 @@ const setScrollLine = () => { | |||
| 332 | textArea.dataset.scrollLine = lineNumber | 365 | textArea.dataset.scrollLine = lineNumber |
| 333 | } | 366 | } |
| 334 | cm.on('scroll', () => { | 367 | cm.on('scroll', () => { |
| 335 | if (cm.hasFocus()) setScrollLine() | 368 | if (cm.hasFocus()) updateCMScrollLine(cm) |
| 336 | }) | 369 | }) |
| 337 | 370 | ||
| 338 | /** Sync scroll from CodeMirror to HTML **/ | 371 | /** Sync scroll from CodeMirror to HTML **/ |
| 339 | new window.MutationObserver(() => { | 372 | new window.MutationObserver(() => { |
| 340 | clearTimeout(textArea.timer) | 373 | clearTimeout(textArea.timer) |
| 341 | textArea.timer = setTimeout( | 374 | textArea.timer = setTimeout( |
| 342 | () => delete textArea.dataset.scrollLine, | 375 | () => { textArea.dataset.scrollLine = '' }, |
| 343 | 1000, | 376 | 1000, |
| 344 | ) | 377 | ) |
| 345 | 378 | ||
| @@ -486,7 +519,7 @@ const updateDumbyMap = (callback = null) => { | |||
| 486 | dumbymap = generateMaps(dumbyContainer) | 519 | dumbymap = generateMaps(dumbyContainer) |
| 487 | // Set onscroll callback | 520 | // Set onscroll callback |
| 488 | const htmlHolder = dumbymap.htmlHolder | 521 | const htmlHolder = dumbymap.htmlHolder |
| 489 | htmlHolder.onscroll = htmlOnScroll(htmlHolder) | 522 | htmlHolder.onscroll = updateScrollLine(htmlHolder) |
| 490 | // Set oncontextmenu callback | 523 | // Set oncontextmenu callback |
| 491 | dumbymap.utils.setContextMenu(menuForEditor) | 524 | dumbymap.utils.setContextMenu(menuForEditor) |
| 492 | 525 | ||
| @@ -497,7 +530,7 @@ updateDumbyMap() | |||
| 497 | // Re-render HTML by editor content | 530 | // Re-render HTML by editor content |
| 498 | cm.on('change', (_, change) => { | 531 | cm.on('change', (_, change) => { |
| 499 | updateDumbyMap(() => { | 532 | updateDumbyMap(() => { |
| 500 | setScrollLine() | 533 | updateCMScrollLine(cm) |
| 501 | }) | 534 | }) |
| 502 | addClassToCodeLines() | 535 | addClassToCodeLines() |
| 503 | completeForCodeBlock(change) | 536 | completeForCodeBlock(change) |