mirror of
https://github.com/go-gitea/gitea.git
synced 2024-11-22 12:52:24 +08:00
Add some handy markdown editor features (#32400)
There were some missing features from EasyMDE: 1. H1 - H3 style 2. Auto add task list 3. Insert a table And added some tests
This commit is contained in:
parent
54146e62c0
commit
af28ce59b8
|
@ -209,6 +209,10 @@ buttons.link.tooltip = Add a link
|
||||||
buttons.list.unordered.tooltip = Add a bullet list
|
buttons.list.unordered.tooltip = Add a bullet list
|
||||||
buttons.list.ordered.tooltip = Add a numbered list
|
buttons.list.ordered.tooltip = Add a numbered list
|
||||||
buttons.list.task.tooltip = Add a list of tasks
|
buttons.list.task.tooltip = Add a list of tasks
|
||||||
|
buttons.table.add.tooltip = Add a table
|
||||||
|
buttons.table.add.insert = Add
|
||||||
|
buttons.table.rows = Rows
|
||||||
|
buttons.table.cols = Columns
|
||||||
buttons.mention.tooltip = Mention a user or team
|
buttons.mention.tooltip = Mention a user or team
|
||||||
buttons.ref.tooltip = Reference an issue or pull request
|
buttons.ref.tooltip = Reference an issue or pull request
|
||||||
buttons.switch_to_legacy.tooltip = Use the legacy editor instead
|
buttons.switch_to_legacy.tooltip = Use the legacy editor instead
|
||||||
|
|
|
@ -21,7 +21,11 @@ Template Attributes:
|
||||||
<div class="ui tab active" data-tab-panel="markdown-writer">
|
<div class="ui tab active" data-tab-panel="markdown-writer">
|
||||||
<markdown-toolbar>
|
<markdown-toolbar>
|
||||||
<div class="markdown-toolbar-group">
|
<div class="markdown-toolbar-group">
|
||||||
<md-header class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.heading.tooltip"}}">{{svg "octicon-heading"}}</md-header>
|
<md-header class="markdown-toolbar-button" level="1" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.heading.tooltip"}}">{{svg "octicon-heading"}}</md-header>
|
||||||
|
<md-header class="markdown-toolbar-button" level="2" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.heading.tooltip"}}">{{svg "octicon-heading"}}</md-header>
|
||||||
|
<md-header class="markdown-toolbar-button" level="3" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.heading.tooltip"}}">{{svg "octicon-heading"}}</md-header>
|
||||||
|
</div>
|
||||||
|
<div class="markdown-toolbar-group">
|
||||||
<md-bold class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.bold.tooltip"}}">{{svg "octicon-bold"}}</md-bold>
|
<md-bold class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.bold.tooltip"}}">{{svg "octicon-bold"}}</md-bold>
|
||||||
<md-italic class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.italic.tooltip"}}">{{svg "octicon-italic"}}</md-italic>
|
<md-italic class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.italic.tooltip"}}">{{svg "octicon-italic"}}</md-italic>
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,6 +38,7 @@ Template Attributes:
|
||||||
<md-unordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.unordered.tooltip"}}">{{svg "octicon-list-unordered"}}</md-unordered-list>
|
<md-unordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.unordered.tooltip"}}">{{svg "octicon-list-unordered"}}</md-unordered-list>
|
||||||
<md-ordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.ordered.tooltip"}}">{{svg "octicon-list-ordered"}}</md-ordered-list>
|
<md-ordered-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.ordered.tooltip"}}">{{svg "octicon-list-ordered"}}</md-ordered-list>
|
||||||
<md-task-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.task.tooltip"}}">{{svg "octicon-tasklist"}}</md-task-list>
|
<md-task-list class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.list.task.tooltip"}}">{{svg "octicon-tasklist"}}</md-task-list>
|
||||||
|
<button class="markdown-toolbar-button markdown-button-table-add" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.table.add.tooltip"}}">{{svg "octicon-table"}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="markdown-toolbar-group">
|
<div class="markdown-toolbar-group">
|
||||||
<md-mention class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.mention.tooltip"}}">{{svg "octicon-mention"}}</md-mention>
|
<md-mention class="markdown-toolbar-button" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.mention.tooltip"}}">{{svg "octicon-mention"}}</md-mention>
|
||||||
|
@ -56,4 +61,12 @@ Template Attributes:
|
||||||
<div class="ui tab markup" data-tab-panel="markdown-previewer">
|
<div class="ui tab markup" data-tab-panel="markdown-previewer">
|
||||||
{{ctx.Locale.Tr "loading"}}
|
{{ctx.Locale.Tr "loading"}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="markdown-add-table-panel tippy-target">
|
||||||
|
<div class="ui form tw-p-4 flex-text-block">
|
||||||
|
<input type="number" name="rows" min="1" value="3" size="3" class="tw-w-24" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.table.rows"}}">
|
||||||
|
x
|
||||||
|
<input type="number" name="cols" min="1" value="3" size="3" class="tw-w-24" data-tooltip-content="{{ctx.Locale.Tr "editor.buttons.table.cols"}}">
|
||||||
|
<button class="ui button primary" type="button">{{ctx.Locale.Tr "editor.buttons.table.add.insert"}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,17 +7,25 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
gap: .5rem;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.combo-markdown-editor .markdown-toolbar-group {
|
.combo-markdown-editor .markdown-toolbar-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
border-left: 1px solid var(--color-secondary);
|
||||||
|
padding: 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combo-markdown-editor .markdown-toolbar-group:first-child {
|
||||||
|
border-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
.combo-markdown-editor .markdown-toolbar-group:last-child {
|
.combo-markdown-editor .markdown-toolbar-group:last-child {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
border-right: none;
|
||||||
|
border-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.combo-markdown-editor .markdown-toolbar-button {
|
.combo-markdown-editor .markdown-toolbar-button {
|
||||||
|
@ -33,6 +41,24 @@
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combo-markdown-editor md-header {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.combo-markdown-editor md-header::after {
|
||||||
|
font-size: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 7px;
|
||||||
|
}
|
||||||
|
.combo-markdown-editor md-header[level="1"]::after {
|
||||||
|
content: "1";
|
||||||
|
}
|
||||||
|
.combo-markdown-editor md-header[level="2"]::after {
|
||||||
|
content: "2";
|
||||||
|
}
|
||||||
|
.combo-markdown-editor md-header[level="3"]::after {
|
||||||
|
content: "3";
|
||||||
|
}
|
||||||
|
|
||||||
.ui.form .combo-markdown-editor textarea.markdown-text-editor,
|
.ui.form .combo-markdown-editor textarea.markdown-text-editor,
|
||||||
.combo-markdown-editor textarea.markdown-text-editor {
|
.combo-markdown-editor textarea.markdown-text-editor {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
padding: 0.5em 0 0;
|
padding: 0.5em 0 0;
|
||||||
border: none;
|
border: none;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
line-height: 1.2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-content-zone .comment {
|
.edit-content-zone .comment {
|
||||||
|
|
|
@ -15,8 +15,14 @@ import {easyMDEToolbarActions} from './EasyMDEToolbarActions.ts';
|
||||||
import {initTextExpander} from './TextExpander.ts';
|
import {initTextExpander} from './TextExpander.ts';
|
||||||
import {showErrorToast} from '../../modules/toast.ts';
|
import {showErrorToast} from '../../modules/toast.ts';
|
||||||
import {POST} from '../../modules/fetch.ts';
|
import {POST} from '../../modules/fetch.ts';
|
||||||
import {EventEditorContentChanged, initTextareaMarkdown, triggerEditorContentChanged} from './EditorMarkdown.ts';
|
import {
|
||||||
|
EventEditorContentChanged,
|
||||||
|
initTextareaMarkdown,
|
||||||
|
textareaInsertText,
|
||||||
|
triggerEditorContentChanged,
|
||||||
|
} from './EditorMarkdown.ts';
|
||||||
import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts';
|
import {DropzoneCustomEventReloadFiles, initDropzone} from '../dropzone.ts';
|
||||||
|
import {createTippy} from '../../modules/tippy.ts';
|
||||||
|
|
||||||
let elementIdCounter = 0;
|
let elementIdCounter = 0;
|
||||||
|
|
||||||
|
@ -122,8 +128,7 @@ export class ComboMarkdownEditor {
|
||||||
const monospaceText = monospaceButton.getAttribute(monospaceEnabled ? 'data-disable-text' : 'data-enable-text');
|
const monospaceText = monospaceButton.getAttribute(monospaceEnabled ? 'data-disable-text' : 'data-enable-text');
|
||||||
monospaceButton.setAttribute('data-tooltip-content', monospaceText);
|
monospaceButton.setAttribute('data-tooltip-content', monospaceText);
|
||||||
monospaceButton.setAttribute('aria-checked', String(monospaceEnabled));
|
monospaceButton.setAttribute('aria-checked', String(monospaceEnabled));
|
||||||
|
monospaceButton.addEventListener('click', (e) => {
|
||||||
monospaceButton?.addEventListener('click', (e) => {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const enabled = localStorage?.getItem('markdown-editor-monospace') !== 'true';
|
const enabled = localStorage?.getItem('markdown-editor-monospace') !== 'true';
|
||||||
localStorage.setItem('markdown-editor-monospace', String(enabled));
|
localStorage.setItem('markdown-editor-monospace', String(enabled));
|
||||||
|
@ -134,12 +139,14 @@ export class ComboMarkdownEditor {
|
||||||
});
|
});
|
||||||
|
|
||||||
const easymdeButton = this.container.querySelector('.markdown-switch-easymde');
|
const easymdeButton = this.container.querySelector('.markdown-switch-easymde');
|
||||||
easymdeButton?.addEventListener('click', async (e) => {
|
easymdeButton.addEventListener('click', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.userPreferredEditor = 'easymde';
|
this.userPreferredEditor = 'easymde';
|
||||||
await this.switchToEasyMDE();
|
await this.switchToEasyMDE();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.initMarkdownButtonTableAdd();
|
||||||
|
|
||||||
initTextareaMarkdown(this.textarea);
|
initTextareaMarkdown(this.textarea);
|
||||||
initTextareaEvents(this.textarea, this.dropzone);
|
initTextareaEvents(this.textarea, this.dropzone);
|
||||||
}
|
}
|
||||||
|
@ -219,6 +226,42 @@ export class ComboMarkdownEditor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateMarkdownTable(rows: number, cols: number): string {
|
||||||
|
const tableLines = [];
|
||||||
|
tableLines.push(
|
||||||
|
`| ${'Header '.repeat(cols).trim().split(' ').join(' | ')} |`,
|
||||||
|
`| ${'--- '.repeat(cols).trim().split(' ').join(' | ')} |`,
|
||||||
|
);
|
||||||
|
for (let i = 0; i < rows; i++) {
|
||||||
|
tableLines.push(`| ${'Cell '.repeat(cols).trim().split(' ').join(' | ')} |`);
|
||||||
|
}
|
||||||
|
return tableLines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
initMarkdownButtonTableAdd() {
|
||||||
|
const addTableButton = this.container.querySelector('.markdown-button-table-add');
|
||||||
|
const addTablePanel = this.container.querySelector('.markdown-add-table-panel');
|
||||||
|
// here the tippy can't attach to the button because the button already owns a tippy for tooltip
|
||||||
|
const addTablePanelTippy = createTippy(addTablePanel, {
|
||||||
|
content: addTablePanel,
|
||||||
|
trigger: 'manual',
|
||||||
|
placement: 'bottom',
|
||||||
|
hideOnClick: true,
|
||||||
|
interactive: true,
|
||||||
|
getReferenceClientRect: () => addTableButton.getBoundingClientRect(),
|
||||||
|
});
|
||||||
|
addTableButton.addEventListener('click', () => addTablePanelTippy.show());
|
||||||
|
|
||||||
|
addTablePanel.querySelector('.ui.button.primary').addEventListener('click', () => {
|
||||||
|
let rows = parseInt(addTablePanel.querySelector<HTMLInputElement>('[name=rows]').value);
|
||||||
|
let cols = parseInt(addTablePanel.querySelector<HTMLInputElement>('[name=cols]').value);
|
||||||
|
rows = Math.max(1, Math.min(100, rows));
|
||||||
|
cols = Math.max(1, Math.min(100, cols));
|
||||||
|
textareaInsertText(this.textarea, `\n${this.generateMarkdownTable(rows, cols)}\n\n`);
|
||||||
|
addTablePanelTippy.hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
switchTabToEditor() {
|
switchTabToEditor() {
|
||||||
this.tabEditor.click();
|
this.tabEditor.click();
|
||||||
}
|
}
|
||||||
|
|
27
web_src/js/features/comp/EditorMarkdown.test.ts
Normal file
27
web_src/js/features/comp/EditorMarkdown.test.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import {initTextareaMarkdown} from './EditorMarkdown.ts';
|
||||||
|
|
||||||
|
test('EditorMarkdown', () => {
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
initTextareaMarkdown(textarea);
|
||||||
|
|
||||||
|
const testInput = (value, expected) => {
|
||||||
|
textarea.value = value;
|
||||||
|
textarea.setSelectionRange(value.length, value.length);
|
||||||
|
const e = new KeyboardEvent('keydown', {key: 'Enter', cancelable: true});
|
||||||
|
textarea.dispatchEvent(e);
|
||||||
|
if (!e.defaultPrevented) textarea.value += '\n';
|
||||||
|
expect(textarea.value).toEqual(expected);
|
||||||
|
};
|
||||||
|
|
||||||
|
testInput('-', '-\n');
|
||||||
|
testInput('1.', '1.\n');
|
||||||
|
|
||||||
|
testInput('- ', '');
|
||||||
|
testInput('1. ', '');
|
||||||
|
|
||||||
|
testInput('- x', '- x\n- ');
|
||||||
|
testInput('- [ ]', '- [ ]\n- ');
|
||||||
|
testInput('- [ ] foo', '- [ ] foo\n- [ ] ');
|
||||||
|
testInput('* [x] foo', '* [x] foo\n* [ ] ');
|
||||||
|
testInput('1. [x] foo', '1. [x] foo\n1. [ ] ');
|
||||||
|
});
|
|
@ -4,6 +4,16 @@ export function triggerEditorContentChanged(target) {
|
||||||
target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true}));
|
target.dispatchEvent(new CustomEvent(EventEditorContentChanged, {bubbles: true}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function textareaInsertText(textarea, value) {
|
||||||
|
const startPos = textarea.selectionStart;
|
||||||
|
const endPos = textarea.selectionEnd;
|
||||||
|
textarea.value = textarea.value.substring(0, startPos) + value + textarea.value.substring(endPos);
|
||||||
|
textarea.selectionStart = startPos;
|
||||||
|
textarea.selectionEnd = startPos + value.length;
|
||||||
|
textarea.focus();
|
||||||
|
triggerEditorContentChanged(textarea);
|
||||||
|
}
|
||||||
|
|
||||||
function handleIndentSelection(textarea, e) {
|
function handleIndentSelection(textarea, e) {
|
||||||
const selStart = textarea.selectionStart;
|
const selStart = textarea.selectionStart;
|
||||||
const selEnd = textarea.selectionEnd;
|
const selEnd = textarea.selectionEnd;
|
||||||
|
@ -46,7 +56,7 @@ function handleIndentSelection(textarea, e) {
|
||||||
triggerEditorContentChanged(textarea);
|
triggerEditorContentChanged(textarea);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewline(textarea, e) {
|
function handleNewline(textarea: HTMLTextAreaElement, e: Event) {
|
||||||
const selStart = textarea.selectionStart;
|
const selStart = textarea.selectionStart;
|
||||||
const selEnd = textarea.selectionEnd;
|
const selEnd = textarea.selectionEnd;
|
||||||
if (selEnd !== selStart) return; // do not process when there is a selection
|
if (selEnd !== selStart) return; // do not process when there is a selection
|
||||||
|
@ -66,9 +76,9 @@ function handleNewline(textarea, e) {
|
||||||
const indention = /^\s*/.exec(line)[0];
|
const indention = /^\s*/.exec(line)[0];
|
||||||
line = line.slice(indention.length);
|
line = line.slice(indention.length);
|
||||||
|
|
||||||
// parse the prefixes: "1. ", "- ", "* ", "[ ] ", "[x] "
|
// parse the prefixes: "1. ", "- ", "* ", there could also be " [ ] " or " [x] " for task lists
|
||||||
// there must be a space after the prefix because none of "1.foo" / "-foo" is a list item
|
// there must be a space after the prefix because none of "1.foo" / "-foo" is a list item
|
||||||
const prefixMatch = /^([0-9]+\.|[-*]|\[ \]|\[x\])\s/.exec(line);
|
const prefixMatch = /^([0-9]+\.|[-*])(\s\[([ x])\])?\s/.exec(line);
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
if (prefixMatch) {
|
if (prefixMatch) {
|
||||||
prefix = prefixMatch[0];
|
prefix = prefixMatch[0];
|
||||||
|
@ -85,8 +95,9 @@ function handleNewline(textarea, e) {
|
||||||
} else {
|
} else {
|
||||||
// start a new line with the same indention and prefix
|
// start a new line with the same indention and prefix
|
||||||
let newPrefix = prefix;
|
let newPrefix = prefix;
|
||||||
if (newPrefix === '[x]') newPrefix = '[ ]';
|
// a simple approach, otherwise it needs to parse the lines after the current line
|
||||||
if (/^\d+\./.test(newPrefix)) newPrefix = `1. `; // a simple approach, otherwise it needs to parse the lines after the current line
|
if (/^\d+\./.test(prefix)) newPrefix = `1. ${newPrefix.slice(newPrefix.indexOf('.') + 2)}`;
|
||||||
|
newPrefix = newPrefix.replace('[x]', '[ ]');
|
||||||
const newLine = `\n${indention}${newPrefix}`;
|
const newLine = `\n${indention}${newPrefix}`;
|
||||||
textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd);
|
textarea.value = value.slice(0, selStart) + newLine + value.slice(selEnd);
|
||||||
textarea.setSelectionRange(selStart + newLine.length, selStart + newLine.length);
|
textarea.setSelectionRange(selStart + newLine.length, selStart + newLine.length);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {imageInfo} from '../../utils/image.ts';
|
import {imageInfo} from '../../utils/image.ts';
|
||||||
import {replaceTextareaSelection} from '../../utils/dom.ts';
|
import {replaceTextareaSelection} from '../../utils/dom.ts';
|
||||||
import {isUrl} from '../../utils/url.ts';
|
import {isUrl} from '../../utils/url.ts';
|
||||||
import {triggerEditorContentChanged} from './EditorMarkdown.ts';
|
import {textareaInsertText, triggerEditorContentChanged} from './EditorMarkdown.ts';
|
||||||
import {
|
import {
|
||||||
DropzoneCustomEventRemovedFile,
|
DropzoneCustomEventRemovedFile,
|
||||||
DropzoneCustomEventUploadDone,
|
DropzoneCustomEventUploadDone,
|
||||||
|
@ -41,14 +41,7 @@ class TextareaEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
insertPlaceholder(value) {
|
insertPlaceholder(value) {
|
||||||
const editor = this.editor;
|
textareaInsertText(this.editor, value);
|
||||||
const startPos = editor.selectionStart;
|
|
||||||
const endPos = editor.selectionEnd;
|
|
||||||
editor.value = editor.value.substring(0, startPos) + value + editor.value.substring(endPos);
|
|
||||||
editor.selectionStart = startPos;
|
|
||||||
editor.selectionEnd = startPos + value.length;
|
|
||||||
editor.focus();
|
|
||||||
triggerEditorContentChanged(editor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replacePlaceholder(oldVal, newVal) {
|
replacePlaceholder(oldVal, newVal) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ type TippyOpts = {
|
||||||
const visibleInstances = new Set<Instance>();
|
const visibleInstances = new Set<Instance>();
|
||||||
const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
|
const arrowSvg = `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`;
|
||||||
|
|
||||||
export function createTippy(target: Element, opts: TippyOpts = {}) {
|
export function createTippy(target: Element, opts: TippyOpts = {}): Instance {
|
||||||
// the callback functions should be destructured from opts,
|
// the callback functions should be destructured from opts,
|
||||||
// because we should use our own wrapper functions to handle them, do not let the user override them
|
// because we should use our own wrapper functions to handle them, do not let the user override them
|
||||||
const {onHide, onShow, onDestroy, role, theme, arrow, ...other} = opts;
|
const {onHide, onShow, onDestroy, role, theme, arrow, ...other} = opts;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user