- Send request to get branch/tag list, use loading icon when waiting for response. - Only fetch when the first time branch/tag list shows. - For backend, removed assignment to `ctx.Data["Branches"]` and `ctx.Data["Tags"]` from `context/repo.go` and passed these data wherever needed. - Changed some `v-if` to `v-show` and used native `svg` as mentioned in https://github.com/go-gitea/gitea/pull/25719#issuecomment-1631712757 to improve perfomance when there are a lot of branches. - Places Used the dropdown component: Repo Home Page <img width="1429" alt="Screen Shot 2023-07-06 at 12 17 51" src="https://github.com/go-gitea/gitea/assets/17645053/6accc7b6-8d37-4e88-ae1a-bd2b3b927ea0"> Commits Page <img width="1431" alt="Screen Shot 2023-07-06 at 12 18 34" src="https://github.com/go-gitea/gitea/assets/17645053/2d0bf306-d1e2-45a8-a784-bc424879f537"> Specific commit -> operations -> cherry-pick <img width="758" alt="Screen Shot 2023-07-06 at 12 23 28" src="https://github.com/go-gitea/gitea/assets/17645053/1e557948-3881-4e45-a625-8ef36d45ae2d"> Release Page <img width="1433" alt="Screen Shot 2023-07-06 at 12 25 05" src="https://github.com/go-gitea/gitea/assets/17645053/3ec82af1-15a4-4162-a50b-04a9502161bb"> - Demo https://github.com/go-gitea/gitea/assets/17645053/d45d266b-3eb0-465a-82f9-57f78dc5f9f3 - Note: UI of dropdown menu could be improved in another PR as it should apply to more dropdown menus. Fix #14180 --------- Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>tags/v1.21.0-rc0
@@ -660,13 +660,6 @@ func RepoAssignment(ctx *Context) context.CancelFunc { | |||
return cancel | |||
} | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) | |||
return cancel | |||
} | |||
ctx.Data["Tags"] = tags | |||
branchOpts := git_model.FindBranchOptions{ | |||
RepoID: ctx.Repo.Repository.ID, | |||
IsDeletedBranch: util.OptionalBoolFalse, | |||
@@ -680,7 +673,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { | |||
return cancel | |||
} | |||
// non empty repo should have at least 1 branch, so this repository's branches haven't been synced yet | |||
// non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet | |||
if branchesTotal == 0 { // fallback to do a sync immediately | |||
branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0) | |||
if err != nil { | |||
@@ -689,24 +682,19 @@ func RepoAssignment(ctx *Context) context.CancelFunc { | |||
} | |||
} | |||
// FIXME: use paganation and async loading | |||
branchOpts.ExcludeBranchNames = []string{ctx.Repo.Repository.DefaultBranch} | |||
brs, err := git_model.FindBranchNames(ctx, branchOpts) | |||
if err != nil { | |||
ctx.ServerError("GetBranches", err) | |||
return cancel | |||
} | |||
// always put default branch on the top | |||
ctx.Data["Branches"] = append(branchOpts.ExcludeBranchNames, brs...) | |||
ctx.Data["BranchesCount"] = branchesTotal | |||
// If not branch selected, try default one. | |||
// If default branch doesn't exist, fall back to some other branch. | |||
// If no branch is set in the request URL, try to guess a default one. | |||
if len(ctx.Repo.BranchName) == 0 { | |||
if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) { | |||
ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch | |||
} else if len(brs) > 0 { | |||
ctx.Repo.BranchName = brs[0] | |||
} else { | |||
ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch() | |||
if ctx.Repo.BranchName == "" { | |||
// If it still can't get a default branch, fall back to default branch from setting. | |||
// Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug. | |||
ctx.Repo.BranchName = setting.Repository.DefaultBranch | |||
} | |||
} | |||
ctx.Repo.RefName = ctx.Repo.BranchName | |||
} |
@@ -754,6 +754,12 @@ func CompareDiff(ctx *context.Context) { | |||
} | |||
ctx.Data["HeadBranches"] = headBranches | |||
// For compare repo branches | |||
PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
headTags, err := repo_model.GetTagNamesByRepoID(ctx, ci.HeadRepo.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) |
@@ -785,18 +785,10 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull | |||
return nil | |||
} | |||
brs, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ | |||
RepoID: ctx.Repo.Repository.ID, | |||
ListOptions: db.ListOptions{ | |||
ListAll: true, | |||
}, | |||
IsDeletedBranch: util.OptionalBoolFalse, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("GetBranches", err) | |||
PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return nil | |||
} | |||
ctx.Data["Branches"] = brs | |||
// Contains true if the user can create issue dependencies | |||
ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.Doer, isPull) | |||
@@ -921,6 +913,13 @@ func NewIssue(ctx *context.Context) { | |||
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false) | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) | |||
return | |||
} | |||
ctx.Data["Tags"] = tags | |||
_, templateErrs := issue_service.GetTemplatesFromDefaultBranch(ctx.Repo.Repository, ctx.Repo.GitRepo) | |||
if errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates); len(errs) > 0 { | |||
for k, v := range errs { | |||
@@ -1918,6 +1917,19 @@ func ViewIssue(ctx *context.Context) { | |||
ctx.Data["ShouldShowCommentType"] = func(commentType issues_model.CommentType) bool { | |||
return hiddenCommentTypes == nil || hiddenCommentTypes.Bit(int(commentType)) == 0 | |||
} | |||
// For sidebar | |||
PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) | |||
return | |||
} | |||
ctx.Data["Tags"] = tags | |||
ctx.HTML(http.StatusOK, tplIssueView) | |||
} |
@@ -729,6 +729,11 @@ func ViewPullCommits(ctx *context.Context) { | |||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) | |||
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.Doer.ID) | |||
// For PR commits page | |||
PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
getBranchData(ctx, issue) | |||
ctx.HTML(http.StatusOK, tplPullCommits) | |||
} | |||
@@ -893,6 +898,11 @@ func ViewPullFiles(ctx *context.Context) { | |||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) | |||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled | |||
// For files changed page | |||
PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
upload.AddUploadContext(ctx, "comment") | |||
ctx.HTML(http.StatusOK, tplPullFiles) |
@@ -352,6 +352,20 @@ func NewRelease(ctx *context.Context) { | |||
ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) | |||
upload.AddUploadContext(ctx, "release") | |||
// For New Release page | |||
PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) | |||
return | |||
} | |||
ctx.Data["Tags"] = tags | |||
ctx.HTML(http.StatusOK, tplReleaseNew) | |||
} | |||
@@ -361,6 +375,13 @@ func NewReleasePost(ctx *context.Context) { | |||
ctx.Data["Title"] = ctx.Tr("repo.release.new_release") | |||
ctx.Data["PageIsReleaseList"] = true | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.ServerError("GetTagNamesByRepoID", err) | |||
return | |||
} | |||
ctx.Data["Tags"] = tags | |||
if ctx.HasError() { | |||
ctx.HTML(http.StatusOK, tplReleaseNew) | |||
return |
@@ -622,3 +622,64 @@ func SearchRepo(ctx *context.Context) { | |||
Data: results, | |||
}) | |||
} | |||
type branchTagSearchResponse struct { | |||
Results []string `json:"results"` | |||
} | |||
// GetBranchesList get branches for current repo' | |||
func GetBranchesList(ctx *context.Context) { | |||
branchOpts := git_model.FindBranchOptions{ | |||
RepoID: ctx.Repo.Repository.ID, | |||
IsDeletedBranch: util.OptionalBoolFalse, | |||
ListOptions: db.ListOptions{ | |||
ListAll: true, | |||
}, | |||
} | |||
branches, err := git_model.FindBranchNames(ctx, branchOpts) | |||
if err != nil { | |||
ctx.JSON(http.StatusInternalServerError, err) | |||
return | |||
} | |||
resp := &branchTagSearchResponse{} | |||
// always put default branch on the top if it exists | |||
if util.SliceContains(branches, ctx.Repo.Repository.DefaultBranch) { | |||
branches = util.SliceRemoveAll(branches, ctx.Repo.Repository.DefaultBranch) | |||
branches = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...) | |||
} | |||
resp.Results = branches | |||
ctx.JSON(http.StatusOK, resp) | |||
} | |||
// GetTagList get tag list for current repo | |||
func GetTagList(ctx *context.Context) { | |||
tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) | |||
if err != nil { | |||
ctx.JSON(http.StatusInternalServerError, err) | |||
return | |||
} | |||
resp := &branchTagSearchResponse{} | |||
resp.Results = tags | |||
ctx.JSON(http.StatusOK, resp) | |||
} | |||
func PrepareBranchList(ctx *context.Context) { | |||
branchOpts := git_model.FindBranchOptions{ | |||
RepoID: ctx.Repo.Repository.ID, | |||
IsDeletedBranch: util.OptionalBoolFalse, | |||
ListOptions: db.ListOptions{ | |||
ListAll: true, | |||
}, | |||
} | |||
brs, err := git_model.FindBranchNames(ctx, branchOpts) | |||
if err != nil { | |||
ctx.ServerError("GetBranches", err) | |||
return | |||
} | |||
// always put default branch on the top if it exists | |||
if util.SliceContains(brs, ctx.Repo.Repository.DefaultBranch) { | |||
brs = util.SliceRemoveAll(brs, ctx.Repo.Repository.DefaultBranch) | |||
brs = append([]string{ctx.Repo.Repository.DefaultBranch}, brs...) | |||
} | |||
ctx.Data["Branches"] = brs | |||
} |
@@ -21,6 +21,7 @@ import ( | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/web" | |||
"code.gitea.io/gitea/routers/web/repo" | |||
"code.gitea.io/gitea/services/forms" | |||
pull_service "code.gitea.io/gitea/services/pull" | |||
"code.gitea.io/gitea/services/repository" | |||
@@ -44,6 +45,11 @@ func ProtectedBranchRules(ctx *context.Context) { | |||
} | |||
ctx.Data["ProtectedBranches"] = rules | |||
repo.PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
ctx.HTML(http.StatusOK, tplBranches) | |||
} | |||
@@ -52,6 +58,11 @@ func SetDefaultBranchPost(ctx *context.Context) { | |||
ctx.Data["Title"] = ctx.Tr("repo.settings.branches.update_default_branch") | |||
ctx.Data["PageIsSettingsBranches"] = true | |||
repo.PrepareBranchList(ctx) | |||
if ctx.Written() { | |||
return | |||
} | |||
repo := ctx.Repo.Repository | |||
switch ctx.FormString("action") { |
@@ -1094,6 +1094,7 @@ func registerRoutes(m *web.Route) { | |||
}, context.RepoRef(), canEnableEditor, context.RepoMustNotBeArchived()) | |||
m.Group("/branches", func() { | |||
m.Get("/list", repo.GetBranchesList) | |||
m.Group("/_new", func() { | |||
m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) | |||
m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch) | |||
@@ -1108,6 +1109,7 @@ func registerRoutes(m *web.Route) { | |||
m.Group("/{username}/{reponame}", func() { | |||
m.Group("/tags", func() { | |||
m.Get("", repo.TagsList) | |||
m.Get("/list", repo.GetTagList) | |||
m.Get(".rss", feedEnabled, repo.TagsListFeedRSS) | |||
m.Get(".atom", feedEnabled, repo.TagsListFeedAtom) | |||
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed), |
@@ -44,8 +44,6 @@ | |||
'tagName': {{.root.TagName}}, | |||
'branchName': {{.root.BranchName}}, | |||
'noTag': {{.noTag}}, | |||
'branches': {{.root.Branches}}, | |||
'tags': {{.root.Tags}}, | |||
'defaultBranch': {{$defaultBranch}}, | |||
'enableFeed': {{.root.EnableFeed}}, | |||
'rssURLPrefix': '{{$.root.RepoLink}}/rss/branch/', |
@@ -3359,3 +3359,7 @@ tbody.commit-list { | |||
font-size: 18px; | |||
margin-left: 4px; | |||
} | |||
#cherry-pick-modal .scrolling.menu { | |||
max-height: 200px; | |||
} |
@@ -11,7 +11,7 @@ | |||
</span> | |||
<svg-icon name="octicon-triangle-down" :size="14" class-name="dropdown icon"/> | |||
</button> | |||
<div class="menu transition" :class="{visible: menuVisible}" v-if="menuVisible" v-cloak> | |||
<div class="menu transition" :class="{visible: menuVisible}" v-show="menuVisible" v-cloak> | |||
<div class="ui icon search input"> | |||
<i class="icon"><svg-icon name="octicon-filter" :size="16"/></i> | |||
<input name="search" ref="searchField" autocomplete="off" v-model="searchTerm" @keydown="keydown($event)" :placeholder="searchFieldPlaceholder"> | |||
@@ -20,13 +20,13 @@ | |||
<div class="header branch-tag-choice"> | |||
<div class="ui grid"> | |||
<div class="two column row"> | |||
<a class="reference column" href="#" @click="createTag = false; mode = 'branches'; focusSearchField()"> | |||
<a class="reference column" href="#" @click="handleTabSwitch('branches')"> | |||
<span class="text" :class="{black: mode === 'branches'}"> | |||
<svg-icon name="octicon-git-branch" :size="16" class-name="gt-mr-2"/>{{ textBranches }} | |||
</span> | |||
</a> | |||
<template v-if="!noTag"> | |||
<a class="reference column" href="#" @click="createTag = true; mode = 'tags'; focusSearchField()"> | |||
<a class="reference column" href="#" @click="handleTabSwitch('tags')"> | |||
<span class="text" :class="{black: mode === 'tags'}"> | |||
<svg-icon name="octicon-tag" :size="16" class-name="gt-mr-2"/>{{ textTags }} | |||
</span> | |||
@@ -37,20 +37,23 @@ | |||
</div> | |||
</template> | |||
<div class="scrolling menu" ref="scrollContainer"> | |||
<svg-icon name="octicon-rss" symbol-id="svg-symbol-octicon-rss"/> | |||
<div class="loading-indicator is-loading" v-if="isLoading"/> | |||
<div v-for="(item, index) in filteredItems" :key="item.name" class="item" :class="{selected: item.selected, active: active === index}" @click="selectItem(item)" :ref="'listItem' + index"> | |||
{{ item.name }} | |||
<a v-if="enableFeed && mode === 'branches'" role="button" class="rss-icon ui compact right" :href="rssURLPrefix + item.url" target="_blank" @click.stop> | |||
<svg-icon name="octicon-rss" :size="14"/> | |||
<a v-show="enableFeed && mode === 'branches'" role="button" class="rss-icon ui compact right" :href="rssURLPrefix + item.url" target="_blank" @click.stop> | |||
<!-- creating a lot of Vue component is pretty slow, so we use a static SVG here --> | |||
<svg width="14" height="14" class="svg octicon-rss"><use href="#svg-symbol-octicon-rss"/></svg> | |||
</a> | |||
</div> | |||
<div class="item" v-if="showCreateNewBranch" :class="{active: active === filteredItems.length}" :ref="'listItem' + filteredItems.length"> | |||
<a href="#" @click="createNewBranch()"> | |||
<div v-show="createTag"> | |||
<div v-show="shouldCreateTag"> | |||
<i class="reference tags icon"/> | |||
<!-- eslint-disable-next-line vue/no-v-html --> | |||
<span v-html="textCreateTag.replace('%s', searchTerm)"/> | |||
</div> | |||
<div v-show="!createTag"> | |||
<div v-show="!shouldCreateTag"> | |||
<svg-icon name="octicon-git-branch"/> | |||
<!-- eslint-disable-next-line vue/no-v-html --> | |||
<span v-html="textCreateBranch.replace('%s', searchTerm)"/> | |||
@@ -64,12 +67,12 @@ | |||
<form ref="newBranchForm" :action="formActionUrl" method="post"> | |||
<input type="hidden" name="_csrf" :value="csrfToken"> | |||
<input type="hidden" name="new_branch_name" v-model="searchTerm"> | |||
<input type="hidden" name="create_tag" v-model="createTag"> | |||
<input type="hidden" name="create_tag" v-model="shouldCreateTag"> | |||
<input type="hidden" name="current_path" v-model="treePath" v-if="treePath"> | |||
</form> | |||
</div> | |||
</div> | |||
<div class="message" v-if="showNoResults"> | |||
<div class="message" v-if="showNoResults && !isLoading"> | |||
{{ noResults }} | |||
</div> | |||
</div> | |||
@@ -81,6 +84,7 @@ import {createApp, nextTick} from 'vue'; | |||
import $ from 'jquery'; | |||
import {SvgIcon} from '../svg.js'; | |||
import {pathEscapeSegments} from '../utils/url.js'; | |||
import {showErrorToast} from '../modules/toast.js'; | |||
const sfc = { | |||
components: {SvgIcon}, | |||
@@ -110,12 +114,16 @@ const sfc = { | |||
formActionUrl() { | |||
return `${this.repoLink}/branches/_new/${this.branchNameSubURL}`; | |||
}, | |||
shouldCreateTag() { | |||
return this.mode === 'tags'; | |||
} | |||
}, | |||
watch: { | |||
menuVisible(visible) { | |||
if (visible) { | |||
this.focusSearchField(); | |||
this.fetchBranchesOrTags(); | |||
} | |||
} | |||
}, | |||
@@ -139,7 +147,6 @@ const sfc = { | |||
} | |||
}); | |||
}, | |||
methods: { | |||
selectItem(item) { | |||
const prev = this.getSelected(); | |||
@@ -246,7 +253,44 @@ const sfc = { | |||
event.preventDefault(); | |||
this.menuVisible = false; | |||
} | |||
} | |||
}, | |||
handleTabSwitch(mode) { | |||
if (this.isLoading) return; | |||
this.mode = mode; | |||
this.focusSearchField(); | |||
this.fetchBranchesOrTags(); | |||
}, | |||
async fetchBranchesOrTags() { | |||
if (!['branches', 'tags'].includes(this.mode) || this.isLoading) return; | |||
// only fetch when branch/tag list has not been initialized | |||
if (this.hasListInitialized[this.mode] || | |||
(this.mode === 'branches' && !this.showBranchesInDropdown) || | |||
(this.mode === 'tags' && this.noTag) | |||
) { | |||
return; | |||
} | |||
this.isLoading = true; | |||
try { | |||
// the "data.defaultBranch" is ambiguous, it could be "branch name" or "tag name" | |||
const reqUrl = `${this.repoLink}/${this.mode}/list`; | |||
const resp = await fetch(reqUrl); | |||
const {results} = await resp.json(); | |||
for (const result of results) { | |||
let selected = false; | |||
if (this.mode === 'branches') { | |||
selected = result === this.defaultBranch; | |||
} else { | |||
selected = result === (this.release ? this.release.tagName : this.defaultBranch); | |||
} | |||
this.items.push({name: result, url: pathEscapeSegments(result), branch: this.mode === 'branches', tag: this.mode === 'tags', selected}); | |||
} | |||
this.hasListInitialized[this.mode] = true; | |||
} catch (e) { | |||
showErrorToast(`Network error when fetching ${this.mode}, error: ${e}`); | |||
} finally { | |||
this.isLoading = false; | |||
} | |||
}, | |||
} | |||
}; | |||
@@ -258,7 +302,6 @@ export function initRepoBranchTagSelector(selector) { | |||
searchTerm: '', | |||
refNameText: '', | |||
menuVisible: false, | |||
createTag: false, | |||
release: null, | |||
isViewTag: false, | |||
@@ -266,27 +309,15 @@ export function initRepoBranchTagSelector(selector) { | |||
isViewTree: false, | |||
active: 0, | |||
isLoading: false, | |||
// This means whether branch list/tag list has initialized | |||
hasListInitialized: { | |||
'branches': false, | |||
'tags': false, | |||
}, | |||
...window.config.pageData.branchDropdownDataList[elIndex], | |||
}; | |||
// the "data.defaultBranch" is ambiguous, it could be "branch name" or "tag name" | |||
if (data.showBranchesInDropdown && data.branches) { | |||
for (const branch of data.branches) { | |||
data.items.push({name: branch, url: pathEscapeSegments(branch), branch: true, tag: false, selected: branch === data.defaultBranch}); | |||
} | |||
} | |||
if (!data.noTag && data.tags) { | |||
for (const tag of data.tags) { | |||
if (data.release) { | |||
data.items.push({name: tag, url: pathEscapeSegments(tag), branch: false, tag: true, selected: tag === data.release.tagName}); | |||
} else { | |||
data.items.push({name: tag, url: pathEscapeSegments(tag), branch: false, tag: true, selected: tag === data.defaultBranch}); | |||
} | |||
} | |||
} | |||
const comp = {...sfc, data() { return data }}; | |||
createApp(comp).mount(elRoot); | |||
} | |||
@@ -302,4 +333,8 @@ export default sfc; // activate IDE's Vue plugin | |||
.menu .item:hover .rss-icon { | |||
display: inline-block; | |||
} | |||
.scrolling.menu .loading-indicator { | |||
height: 4em; | |||
} | |||
</style> |
@@ -1,5 +1,6 @@ | |||
import {htmlEscape} from 'escape-goat'; | |||
import {svg} from '../svg.js'; | |||
import Toastify from 'toastify-js'; | |||
const levels = { | |||
info: { | |||
@@ -23,7 +24,6 @@ const levels = { | |||
async function showToast(message, level, {gravity, position, duration, ...other} = {}) { | |||
if (!message) return; | |||
const {default: Toastify} = await import(/* webpackChunkName: 'toastify' */'toastify-js'); | |||
const {icon, background, duration: levelDuration} = levels[level ?? 'info']; | |||
const toast = Toastify({ |
@@ -185,9 +185,10 @@ export const SvgIcon = { | |||
name: {type: String, required: true}, | |||
size: {type: Number, default: 16}, | |||
className: {type: String, default: ''}, | |||
symbolId: {type: String} | |||
}, | |||
render() { | |||
const {svgOuter, svgInnerHtml} = svgParseOuterInner(this.name); | |||
let {svgOuter, svgInnerHtml} = svgParseOuterInner(this.name); | |||
// https://vuejs.org/guide/extras/render-function.html#creating-vnodes | |||
// the `^` is used for attr, set SVG attributes like 'width', `aria-hidden`, `viewBox`, etc | |||
const attrs = {}; | |||
@@ -207,7 +208,10 @@ export const SvgIcon = { | |||
if (this.className) { | |||
classes.push(...this.className.split(/\s+/).filter(Boolean)); | |||
} | |||
if (this.symbolId) { | |||
classes.push('gt-hidden', 'svg-symbol-container'); | |||
svgInnerHtml = `<symbol id="${this.symbolId}" viewBox="${attrs['^viewBox']}">${svgInnerHtml}</symbol>`; | |||
} | |||
// create VNode | |||
return h('svg', { | |||
...attrs, |