mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Batch delete issue and improve tippy opts (#25253)
1. Add "batch delete" button for selected issues, close #22273 2. Address the review in https://github.com/go-gitea/gitea/pull/25219#discussion_r1229266083
This commit is contained in:
		@@ -140,6 +140,10 @@ func (b *Base) JSONRedirect(redirect string) {
 | 
				
			|||||||
	b.JSON(http.StatusOK, map[string]any{"redirect": redirect})
 | 
						b.JSON(http.StatusOK, map[string]any{"redirect": redirect})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) JSONOK() {
 | 
				
			||||||
 | 
						b.JSON(http.StatusOK, map[string]any{"ok": true}) // this is only a dummy response, frontend seldom uses it
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Base) JSONError(msg string) {
 | 
					func (b *Base) JSONError(msg string) {
 | 
				
			||||||
	b.JSON(http.StatusBadRequest, map[string]any{"errorMessage": msg})
 | 
						b.JSON(http.StatusBadRequest, map[string]any{"errorMessage": msg})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -130,6 +130,8 @@ show_timestamps = Show timestamps
 | 
				
			|||||||
show_log_seconds = Show seconds
 | 
					show_log_seconds = Show seconds
 | 
				
			||||||
show_full_screen = Show full screen
 | 
					show_full_screen = Show full screen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					confirm_delete_selected = Confirm to delete all selected items?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[aria]
 | 
					[aria]
 | 
				
			||||||
navbar = Navigation Bar
 | 
					navbar = Navigation Bar
 | 
				
			||||||
footer = Footer
 | 
					footer = Footer
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2705,6 +2705,20 @@ func ListIssues(ctx *context.Context) {
 | 
				
			|||||||
	ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
 | 
						ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BatchDeleteIssues(ctx *context.Context) {
 | 
				
			||||||
 | 
						issues := getActionIssues(ctx)
 | 
				
			||||||
 | 
						if ctx.Written() {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, issue := range issues {
 | 
				
			||||||
 | 
							if err := issue_service.DeleteIssue(ctx, ctx.Doer, ctx.Repo.GitRepo, issue); err != nil {
 | 
				
			||||||
 | 
								ctx.ServerError("DeleteIssue", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.JSONOK()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateIssueStatus change issue's status
 | 
					// UpdateIssueStatus change issue's status
 | 
				
			||||||
func UpdateIssueStatus(ctx *context.Context) {
 | 
					func UpdateIssueStatus(ctx *context.Context) {
 | 
				
			||||||
	issues := getActionIssues(ctx)
 | 
						issues := getActionIssues(ctx)
 | 
				
			||||||
@@ -2740,9 +2754,7 @@ func UpdateIssueStatus(ctx *context.Context) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.JSON(http.StatusOK, map[string]interface{}{
 | 
						ctx.JSONOK()
 | 
				
			||||||
		"ok": true,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewComment create a comment for issue
 | 
					// NewComment create a comment for issue
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1024,6 +1024,7 @@ func registerRoutes(m *web.Route) {
 | 
				
			|||||||
			m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
 | 
								m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
 | 
				
			||||||
			m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
 | 
								m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
 | 
				
			||||||
			m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
 | 
								m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
 | 
				
			||||||
 | 
								m.Post("/delete", reqRepoAdmin, repo.BatchDeleteIssues)
 | 
				
			||||||
			m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation)
 | 
								m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation)
 | 
				
			||||||
			m.Post("/attachments", repo.UploadIssueAttachment)
 | 
								m.Post("/attachments", repo.UploadIssueAttachment)
 | 
				
			||||||
			m.Post("/attachments/remove", repo.DeleteAttachment)
 | 
								m.Post("/attachments/remove", repo.DeleteAttachment)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,9 @@
 | 
				
			|||||||
			It might be renamed to "link-fetch-action" to match the "form-fetch-action".
 | 
								It might be renamed to "link-fetch-action" to match the "form-fetch-action".
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div>
 | 
							<div>
 | 
				
			||||||
			<button class="link-action" data-url="fetch-action-test?k=1">test</button>
 | 
								<button class="link-action" data-url="fetch-action-test?k=1">test action</button>
 | 
				
			||||||
 | 
								<button class="link-action" data-url="fetch-action-test?k=1" data-modal-confirm="confirm?">test with confirm</button>
 | 
				
			||||||
 | 
								<button class="ui red button link-action" data-url="fetch-action-test?k=1" data-modal-confirm="confirm?">test with risky confirm</button>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -282,9 +282,15 @@
 | 
				
			|||||||
					{{if not .Repository.IsArchived}}
 | 
										{{if not .Repository.IsArchived}}
 | 
				
			||||||
					<!-- Action Button -->
 | 
										<!-- Action Button -->
 | 
				
			||||||
					{{if .IsShowClosed}}
 | 
										{{if .IsShowClosed}}
 | 
				
			||||||
						<button class="ui green active basic button issue-action gt-ml-auto" data-action="open" data-url="{{$.RepoLink}}/issues/status">{{.locale.Tr "repo.issues.action_open"}}</button>
 | 
											<button class="ui green basic button issue-action gt-ml-auto" data-action="open" data-url="{{$.RepoLink}}/issues/status">{{.locale.Tr "repo.issues.action_open"}}</button>
 | 
				
			||||||
					{{else}}
 | 
										{{else}}
 | 
				
			||||||
						<button class="ui red active basic button issue-action gt-ml-auto" data-action="close" data-url="{{$.RepoLink}}/issues/status">{{.locale.Tr "repo.issues.action_close"}}</button>
 | 
											<button class="ui red basic button issue-action gt-ml-auto" data-action="close" data-url="{{$.RepoLink}}/issues/status">{{.locale.Tr "repo.issues.action_close"}}</button>
 | 
				
			||||||
 | 
										{{end}}
 | 
				
			||||||
 | 
										{{if $.IsRepoAdmin}}
 | 
				
			||||||
 | 
											<button class="ui red button issue-action gt-ml-auto"
 | 
				
			||||||
 | 
												data-action="delete" data-url="{{$.RepoLink}}/issues/delete"
 | 
				
			||||||
 | 
												data-action-delete-confirm="{{.locale.Tr "confirm_delete_selected"}}"
 | 
				
			||||||
 | 
											>{{.locale.Tr "repo.issues.delete"}}</button>
 | 
				
			||||||
					{{end}}
 | 
										{{end}}
 | 
				
			||||||
					<!-- Labels -->
 | 
										<!-- Labels -->
 | 
				
			||||||
					<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">
 | 
										<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import {svg} from '../svg.js';
 | 
				
			|||||||
import {hideElem, showElem, toggleElem} from '../utils/dom.js';
 | 
					import {hideElem, showElem, toggleElem} from '../utils/dom.js';
 | 
				
			||||||
import {htmlEscape} from 'escape-goat';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
import {createTippy} from '../modules/tippy.js';
 | 
					import {createTippy} from '../modules/tippy.js';
 | 
				
			||||||
 | 
					import {confirmModal} from './comp/ConfirmModal.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
 | 
					const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -264,7 +265,7 @@ export function initGlobalDropzone() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function linkAction(e) {
 | 
					async function linkAction(e) {
 | 
				
			||||||
  e.preventDefault();
 | 
					  e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // A "link-action" can post AJAX request to its "data-url"
 | 
					  // A "link-action" can post AJAX request to its "data-url"
 | 
				
			||||||
@@ -291,33 +292,16 @@ function linkAction(e) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const modalConfirmHtml = htmlEscape($this.attr('data-modal-confirm') || '');
 | 
					  const modalConfirmContent = htmlEscape($this.attr('data-modal-confirm') || '');
 | 
				
			||||||
  if (!modalConfirmHtml) {
 | 
					  if (!modalConfirmContent) {
 | 
				
			||||||
    doRequest();
 | 
					    doRequest();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const okButtonColor = $this.hasClass('red') || $this.hasClass('yellow') || $this.hasClass('orange') || $this.hasClass('negative') ? 'orange' : 'green';
 | 
					  const isRisky = $this.hasClass('red') || $this.hasClass('yellow') || $this.hasClass('orange') || $this.hasClass('negative');
 | 
				
			||||||
 | 
					  if (await confirmModal({content: modalConfirmContent, buttonColor: isRisky ? 'orange' : 'green'})) {
 | 
				
			||||||
  const $modal = $(`
 | 
					    doRequest();
 | 
				
			||||||
<div class="ui g-modal-confirm modal">
 | 
					  }
 | 
				
			||||||
  <div class="content">${modalConfirmHtml}</div>
 | 
					 | 
				
			||||||
  <div class="actions">
 | 
					 | 
				
			||||||
    <button class="ui basic cancel button">${svg('octicon-x')} ${i18n.modal_cancel}</button>
 | 
					 | 
				
			||||||
    <button class="ui ${okButtonColor} ok button">${svg('octicon-check')} ${i18n.modal_confirm}</button>
 | 
					 | 
				
			||||||
  </div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  $modal.appendTo(document.body);
 | 
					 | 
				
			||||||
  $modal.modal({
 | 
					 | 
				
			||||||
    onApprove() {
 | 
					 | 
				
			||||||
      doRequest();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    onHidden() {
 | 
					 | 
				
			||||||
      $modal.remove();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  }).modal('show');
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function initGlobalLinkActions() {
 | 
					export function initGlobalLinkActions() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										30
									
								
								web_src/js/features/comp/ConfirmModal.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web_src/js/features/comp/ConfirmModal.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					import $ from 'jquery';
 | 
				
			||||||
 | 
					import {svg} from '../../svg.js';
 | 
				
			||||||
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const {i18n} = window.config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function confirmModal(opts = {content: '', buttonColor: 'green'}) {
 | 
				
			||||||
 | 
					  return new Promise((resolve) => {
 | 
				
			||||||
 | 
					    const $modal = $(`
 | 
				
			||||||
 | 
					<div class="ui g-modal-confirm modal">
 | 
				
			||||||
 | 
					  <div class="content">${htmlEscape(opts.content)}</div>
 | 
				
			||||||
 | 
					  <div class="actions">
 | 
				
			||||||
 | 
					    <button class="ui basic cancel button">${svg('octicon-x')} ${i18n.modal_cancel}</button>
 | 
				
			||||||
 | 
					    <button class="ui ${opts.buttonColor || 'green'} ok button">${svg('octicon-check')} ${i18n.modal_confirm}</button>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $modal.appendTo(document.body);
 | 
				
			||||||
 | 
					    $modal.modal({
 | 
				
			||||||
 | 
					      onApprove() {
 | 
				
			||||||
 | 
					        resolve(true);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      onHidden() {
 | 
				
			||||||
 | 
					        $modal.remove();
 | 
				
			||||||
 | 
					        resolve(false);
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    }).modal('show');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,6 +3,7 @@ import {updateIssuesMeta} from './repo-issue.js';
 | 
				
			|||||||
import {toggleElem} from '../utils/dom.js';
 | 
					import {toggleElem} from '../utils/dom.js';
 | 
				
			||||||
import {htmlEscape} from 'escape-goat';
 | 
					import {htmlEscape} from 'escape-goat';
 | 
				
			||||||
import {Sortable} from 'sortablejs';
 | 
					import {Sortable} from 'sortablejs';
 | 
				
			||||||
 | 
					import {confirmModal} from './comp/ConfirmModal.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function initRepoIssueListCheckboxes() {
 | 
					function initRepoIssueListCheckboxes() {
 | 
				
			||||||
  const $issueSelectAll = $('.issue-checkbox-all');
 | 
					  const $issueSelectAll = $('.issue-checkbox-all');
 | 
				
			||||||
@@ -36,19 +37,36 @@ function initRepoIssueListCheckboxes() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  $('.issue-action').on('click', async function (e) {
 | 
					  $('.issue-action').on('click', async function (e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const url = this.getAttribute('data-url');
 | 
				
			||||||
    let action = this.getAttribute('data-action');
 | 
					    let action = this.getAttribute('data-action');
 | 
				
			||||||
    let elementId = this.getAttribute('data-element-id');
 | 
					    let elementId = this.getAttribute('data-element-id');
 | 
				
			||||||
    const url = this.getAttribute('data-url');
 | 
					    let issueIDs = [];
 | 
				
			||||||
    const issueIDs = $('.issue-checkbox:checked').map((_, el) => {
 | 
					    for (const el of document.querySelectorAll('.issue-checkbox:checked')) {
 | 
				
			||||||
      return el.getAttribute('data-issue-id');
 | 
					      issueIDs.push(el.getAttribute('data-issue-id'));
 | 
				
			||||||
    }).get().join(',');
 | 
					    }
 | 
				
			||||||
    if (elementId === '0' && url.slice(-9) === '/assignee') {
 | 
					    issueIDs = issueIDs.join(',');
 | 
				
			||||||
 | 
					    if (!issueIDs) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // for assignee
 | 
				
			||||||
 | 
					    if (elementId === '0' && url.endsWith('/assignee')) {
 | 
				
			||||||
      elementId = '';
 | 
					      elementId = '';
 | 
				
			||||||
      action = 'clear';
 | 
					      action = 'clear';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // for toggle
 | 
				
			||||||
    if (action === 'toggle' && e.altKey) {
 | 
					    if (action === 'toggle' && e.altKey) {
 | 
				
			||||||
      action = 'toggle-alt';
 | 
					      action = 'toggle-alt';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // for delete
 | 
				
			||||||
 | 
					    if (action === 'delete') {
 | 
				
			||||||
 | 
					      const confirmText = e.target.getAttribute('data-action-delete-confirm');
 | 
				
			||||||
 | 
					      if (!await confirmModal({content: confirmText, buttonColor: 'orange'})) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    updateIssuesMeta(
 | 
					    updateIssuesMeta(
 | 
				
			||||||
      url,
 | 
					      url,
 | 
				
			||||||
      action,
 | 
					      action,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,11 +3,9 @@ import tippy from 'tippy.js';
 | 
				
			|||||||
const visibleInstances = new Set();
 | 
					const visibleInstances = new Set();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function createTippy(target, opts = {}) {
 | 
					export function createTippy(target, opts = {}) {
 | 
				
			||||||
  const {role, content, onHide: optsOnHide, onDestroy: optsOnDestroy, onShow: optOnShow} = opts;
 | 
					  // the callback functions should be destructured from opts,
 | 
				
			||||||
  delete opts.onHide;
 | 
					  // because we should use our own wrapper functions to handle them, do not let the user override them
 | 
				
			||||||
  delete opts.onDestroy;
 | 
					  const {onHide, onShow, onDestroy, ...other} = opts;
 | 
				
			||||||
  delete opts.onShow;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const instance = tippy(target, {
 | 
					  const instance = tippy(target, {
 | 
				
			||||||
    appendTo: document.body,
 | 
					    appendTo: document.body,
 | 
				
			||||||
    animation: false,
 | 
					    animation: false,
 | 
				
			||||||
@@ -18,11 +16,11 @@ export function createTippy(target, opts = {}) {
 | 
				
			|||||||
    maxWidth: 500, // increase over default 350px
 | 
					    maxWidth: 500, // increase over default 350px
 | 
				
			||||||
    onHide: (instance) => {
 | 
					    onHide: (instance) => {
 | 
				
			||||||
      visibleInstances.delete(instance);
 | 
					      visibleInstances.delete(instance);
 | 
				
			||||||
      return optsOnHide?.(instance);
 | 
					      return onHide?.(instance);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    onDestroy: (instance) => {
 | 
					    onDestroy: (instance) => {
 | 
				
			||||||
      visibleInstances.delete(instance);
 | 
					      visibleInstances.delete(instance);
 | 
				
			||||||
      return optsOnDestroy?.(instance);
 | 
					      return onDestroy?.(instance);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    onShow: (instance) => {
 | 
					    onShow: (instance) => {
 | 
				
			||||||
      // hide other tooltip instances so only one tooltip shows at a time
 | 
					      // hide other tooltip instances so only one tooltip shows at a time
 | 
				
			||||||
@@ -32,19 +30,19 @@ export function createTippy(target, opts = {}) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      visibleInstances.add(instance);
 | 
					      visibleInstances.add(instance);
 | 
				
			||||||
      return optOnShow?.(instance);
 | 
					      return onShow?.(instance);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    arrow: `<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>`,
 | 
					    arrow: `<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>`,
 | 
				
			||||||
    role: 'menu', // HTML role attribute, only tooltips should use "tooltip"
 | 
					    role: 'menu', // HTML role attribute, only tooltips should use "tooltip"
 | 
				
			||||||
    theme: role || 'menu', // CSS theme, we support either "tooltip" or "menu"
 | 
					    theme: other.role || 'menu', // CSS theme, we support either "tooltip" or "menu"
 | 
				
			||||||
    ...opts,
 | 
					    ...other,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // for popups where content refers to a DOM element, we use the 'tippy-target' class
 | 
					  // for popups where content refers to a DOM element, we use the 'tippy-target' class
 | 
				
			||||||
  // to initially hide the content, now we can remove it as the content has been removed
 | 
					  // to initially hide the content, now we can remove it as the content has been removed
 | 
				
			||||||
  // from the DOM by tippy
 | 
					  // from the DOM by tippy
 | 
				
			||||||
  if (content instanceof Element) {
 | 
					  if (other.content instanceof Element) {
 | 
				
			||||||
    content.classList.remove('tippy-target');
 | 
					    other.content.classList.remove('tippy-target');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return instance;
 | 
					  return instance;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user