-
+
-
+
+
-
+
+
+
{{end}}
diff --git a/templates/user/auth/signup_openid_navbar.tmpl b/templates/user/auth/signup_openid_navbar.tmpl
index 075f2e4d7b..9cf81b048f 100644
--- a/templates/user/auth/signup_openid_navbar.tmpl
+++ b/templates/user/auth/signup_openid_navbar.tmpl
@@ -1,5 +1,5 @@
-
{{ctx.Locale.Tr "auth.tab_signin"}}
@@ -20,5 +20,5 @@
{{end}}
-
-
+
+
+
diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl
index cf5e21fa62..275c4e295e 100644
--- a/templates/user/overview/header.tmpl
+++ b/templates/user/overview/header.tmpl
@@ -1,49 +1,50 @@
-
{{ctx.Locale.Tr "auth.openid_connect_title"}}
@@ -9,4 +9,4 @@
{{end}}
-
- {{if and .HasProfileReadme .ContextUser.IsIndividual}}
-
- {{svg "octicon-info"}} {{ctx.Locale.Tr "user.overview"}}
-
- {{end}}
-
- {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}}
- {{if .RepoCount}}
-
+
+
diff --git a/web_src/css/base.css b/web_src/css/base.css
index cc74db820f..e746556cd3 100644
--- a/web_src/css/base.css
+++ b/web_src/css/base.css
@@ -259,7 +259,7 @@ a.label,
}
.page-content .header-wrapper,
-.page-content .new-menu {
+.page-content overflow-menu {
margin-top: -15px !important;
padding-top: 15px !important;
}
@@ -1358,75 +1358,21 @@ strong.attention-caution, span.attention-caution {
}
}
-.ui.menu.new-menu {
- margin-bottom: 15px;
- background: var(--color-header-wrapper);
+overflow-menu {
+ margin-bottom: 15px !important;
border-bottom: 1px solid var(--color-secondary) !important;
- overflow: auto;
-}
-
-.ui.menu.new-menu .new-menu-inner {
display: flex;
- margin-left: auto;
- margin-right: auto;
- overflow-x: auto;
- width: 100%;
- mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 60px), transparent 100%);
- -webkit-mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 60px), transparent 100%);
}
-.ui.menu.new-menu .item {
+overflow-menu .overflow-menu-items {
+ display: flex;
+ flex: 1;
+}
+
+overflow-menu .overflow-menu-items .item {
margin-bottom: 0 !important; /* reset fomantic's margin, because the active menu has special bottom border */
}
-@media (max-width: 767.98px) {
- .ui.menu.new-menu .item {
- width: auto !important;
- }
-}
-
-.ui.menu.new-menu .item:first-child {
- margin-left: auto; /* "justify-content: center" doesn't work with "overflow: auto", so use margin: auto */
-}
-
-.ui.menu.new-menu .item:last-child {
- padding-right: 30px !important;
- margin-right: auto;
-}
-
-.ui.menu.new-menu::-webkit-scrollbar {
- height: 6px;
- display: none;
-}
-
-.ui.menu.new-menu::-webkit-scrollbar-track {
- background: none !important;
-}
-
-.ui.menu.new-menu::-webkit-scrollbar-thumb {
- box-shadow: none !important;
-}
-
-.ui.menu.new-menu:hover::-webkit-scrollbar {
- display: block;
-}
-
-.repos-search {
- padding-bottom: 0 !important;
-}
-
-.repos-filter {
- margin-top: 0 !important;
- border-bottom-width: 0 !important;
- margin-bottom: 2px !important;
- justify-content: space-evenly;
-}
-
-.ui.secondary.pointing.menu.repos-filter .item {
- padding-left: 4.5px;
- padding-right: 4.5px;
-}
-
.activity-bar-graph {
background-color: var(--color-primary);
color: var(--color-primary-contrast);
@@ -1932,7 +1878,6 @@ table th[data-sortt-desc] .svg {
background: var(--color-body);
border-color: var(--color-secondary);
color: var(--color-text);
- margin-top: 1px; /* offset fomantic's margin-bottom: -1px */
}
.ui.segment .ui.tabular.menu .active.item,
diff --git a/web_src/css/modules/tippy.css b/web_src/css/modules/tippy.css
index d65ecc89fb..76d36b4293 100644
--- a/web_src/css/modules/tippy.css
+++ b/web_src/css/modules/tippy.css
@@ -5,6 +5,11 @@
display: none !important;
}
+/* show target element once it's been moved by tippy.js */
+.tippy-content .tippy-target {
+ display: unset !important;
+}
+
[data-tippy-root] {
max-width: calc(100vw - 32px);
}
@@ -46,18 +51,40 @@
.tippy-box[data-theme="menu"] {
background-color: var(--color-menu);
color: var(--color-text);
+ box-shadow: 0 6px 18px var(--color-shadow);
}
.tippy-box[data-theme="menu"] .tippy-content {
- padding: 0;
+ padding: 4px 0;
}
.tippy-box[data-theme="menu"] .tippy-svg-arrow-inner {
fill: var(--color-menu);
}
+.tippy-box[data-theme="menu"] .item {
+ display: flex;
+ align-items: center;
+ padding: 9px 18px;
+ color: inherit;
+ text-decoration: none;
+ gap: 10px;
+}
+
+.tippy-box[data-theme="menu"] .item:hover {
+ background: var(--color-hover);
+}
+
+.tippy-box[data-theme="menu"] .item:focus {
+ background: var(--color-active);
+}
+
/* box-with-header theme to look like .ui.attached.segment. can contain .ui.attached.header */
+.tippy-box[data-theme="box-with-header"] {
+ box-shadow: 0 6px 18px var(--color-shadow);
+}
+
.tippy-box[data-theme="box-with-header"] .tippy-content {
background: var(--color-box-body);
border-radius: var(--border-radius);
diff --git a/web_src/css/repo.css b/web_src/css/repo.css
index dda2439daf..d4b43e7e3a 100644
--- a/web_src/css/repo.css
+++ b/web_src/css/repo.css
@@ -2782,16 +2782,6 @@ tbody.commit-list {
border-left: 1px solid var(--color-secondary);
}
-.repository .ui.menu.new-menu {
- background: none !important;
-}
-
-@media (max-width: 1200px) {
- .repository .ui.menu.new-menu::after {
- background: none !important;
- }
-}
-
.migrate-entries {
display: grid !important;
grid-template-columns: repeat(3, 1fr);
diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css
index 3400284e4b..a262b5b3e9 100644
--- a/web_src/css/repo/header.css
+++ b/web_src/css/repo/header.css
@@ -74,17 +74,6 @@
background-color: var(--color-header-wrapper);
}
-.repository .header-wrapper .new-menu {
- padding-top: 0 !important;
- margin-top: 0 !important;
- margin-bottom: 0 !important;
-}
-
-.repository .header-wrapper .new-menu .item {
- margin-left: 0 !important;
- margin-right: 0 !important;
-}
-
@media (max-width: 767.98px) {
.repo-header .flex-item {
flex-grow: 1;
diff --git a/web_src/css/repo/linebutton.css b/web_src/css/repo/linebutton.css
index 1e5e51eac5..79be5a7a9e 100644
--- a/web_src/css/repo/linebutton.css
+++ b/web_src/css/repo/linebutton.css
@@ -2,11 +2,6 @@
color: var(--color-text-dark) !important;
}
-.code-line-menu {
- width: auto !important;
- border: none !important; /* the border is provided by tippy, not using the `.ui.menu` border */
-}
-
.code-line-button {
background-color: var(--color-menu);
color: var(--color-text-light);
diff --git a/web_src/css/shared/repoorg.css b/web_src/css/shared/repoorg.css
index 7f0a805d0f..5573ae47b8 100644
--- a/web_src/css/shared/repoorg.css
+++ b/web_src/css/shared/repoorg.css
@@ -5,13 +5,6 @@
margin-left: 15px;
}
-.repository .ui.secondary.stackable.pointing.menu,
-.organization .ui.secondary.stackable.pointing.menu {
- flex-wrap: wrap;
- margin-top: 5px;
- margin-bottom: 10px;
-}
-
.repository .ui.tabs.container,
.organization .ui.tabs.container {
margin-top: 14px;
diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue
index 6f742bbea0..29f278d8ae 100644
--- a/web_src/js/components/DashboardRepoList.vue
+++ b/web_src/js/components/DashboardRepoList.vue
@@ -384,28 +384,30 @@ export default sfc; // activate the IDE's Vue plugin
{{.RepoCount}}
+
+ {{if and .HasProfileReadme .ContextUser.IsIndividual}}
+
+ {{svg "octicon-info"}} {{ctx.Locale.Tr "user.overview"}}
+
{{end}}
-
- {{if or .ContextUser.IsIndividual .CanReadProjects}}
-
- {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}}
- {{if .ProjectCount}}
-
+ {{.ProjectCount}}
+
+ {{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}}
+ {{if .RepoCount}}
+ {{.RepoCount}}
+ {{end}}
+
+ {{if or .ContextUser.IsIndividual .CanReadProjects}}
+
+ {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}}
+ {{if .ProjectCount}}
+ {{.ProjectCount}}
+ {{end}}
+
{{end}}
-
- {{end}}
- {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}}
-
- {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}}
-
- {{end}}
- {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}}
-
- {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}}
-
- {{end}}
-
- {{if .ContextUser.IsIndividual}}
-
- {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}}
-
- {{if not .DisableStars}}
+ {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}}
+
+ {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}}
+
+ {{end}}
+ {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}}
+
+ {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}}
+
+ {{end}}
+ {{if .ContextUser.IsIndividual}}
+
+ {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}}
+
+ {{if not .DisableStars}}
{{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}}
{{if .ContextUser.NumStars}}
{{.ContextUser.NumStars}}
{{end}}
- {{else}}
+ {{else}}
{{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}}
+ {{end}}
{{end}}
- {{end}}
-
-
- {{ textAll }}
-
+ {{ repoTypeCount }}
-
-
- {{ textSources }}
- {{ repoTypeCount }}
-
-
- {{ textForks }}
- {{ repoTypeCount }}
-
-
- {{ textMirrors }}
- {{ repoTypeCount }}
-
-
- {{ textCollaborative }}
- {{ repoTypeCount }}
-
- -
@@ -501,6 +503,22 @@ ul li:not(:last-child) {
border-bottom: 1px solid var(--color-secondary);
}
+.repos-search {
+ padding-bottom: 0 !important;
+}
+
+.repos-filter {
+ padding-top: 0 !important;
+ margin-top: 0 !important;
+ border-bottom-width: 0 !important;
+ margin-bottom: 2px !important;
+}
+
+.repos-filter .item {
+ padding-left: 6px !important;
+ padding-right: 6px !important;
+}
+
.repo-list-link {
min-width: 0; /* for text truncation */
display: flex;
diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js
index 489afc0ae1..e7eb39f457 100644
--- a/web_src/js/modules/tippy.js
+++ b/web_src/js/modules/tippy.js
@@ -7,7 +7,8 @@ const visibleInstances = new Set();
export function createTippy(target, 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
- const {onHide, onShow, onDestroy, ...other} = opts;
+ const {onHide, onShow, onDestroy, role, theme, ...other} = opts;
+
const instance = tippy(target, {
appendTo: document.body,
animation: false,
@@ -35,17 +36,14 @@ export function createTippy(target, opts = {}) {
return onShow?.(instance);
},
arrow: ``,
- role: 'menu', // HTML role attribute, only tooltips should use "tooltip"
- theme: other.role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header"
+ role: role || 'menu', // HTML role attribute
+ theme: theme || role || 'menu', // CSS theme, either "tooltip", "menu" or "box-with-header"
plugins: [followCursor],
...other,
});
- // 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
- // from the DOM by tippy
- if (other.content instanceof Element) {
- other.content.classList.remove('tippy-target');
+ if (role === 'menu') {
+ target.setAttribute('aria-haspopup', 'true');
}
return instance;
diff --git a/web_src/js/webcomponents/README.md b/web_src/js/webcomponents/README.md
index 0fde507310..45af58e1d2 100644
--- a/web_src/js/webcomponents/README.md
+++ b/web_src/js/webcomponents/README.md
@@ -6,7 +6,6 @@ https://developer.mozilla.org/en-US/docs/Web/Web_Components
# Guidelines
-* These components are loaded in `` (before DOM body),
- so they should have their own dependencies and should be very light,
- then they won't affect the page loading time too much.
-* If the component is not a public one, it's suggested to have its own `Gitea` or `gitea-` prefix to avoid conflicts.
+* These components are loaded in `` (before DOM body) in a separate entry point, they need to be lightweight to not affect the page loading time too much.
+* Do not import `svg.js` into a web component because that file is currently not tree-shakeable, import svg files individually insteat.
+* All our components must be added to `webpack.config.js` so they work correctly in Vue.
diff --git a/web_src/js/webcomponents/GiteaAbsoluteDate.js b/web_src/js/webcomponents/absolute-date.js
similarity index 95%
rename from web_src/js/webcomponents/GiteaAbsoluteDate.js
rename to web_src/js/webcomponents/absolute-date.js
index 660aa99d07..d12ea0a437 100644
--- a/web_src/js/webcomponents/GiteaAbsoluteDate.js
+++ b/web_src/js/webcomponents/absolute-date.js
@@ -1,4 +1,4 @@
-window.customElements.define('gitea-absolute-date', class extends HTMLElement {
+window.customElements.define('absolute-date', class extends HTMLElement {
static observedAttributes = ['date', 'year', 'month', 'weekday', 'day'];
update = () => {
diff --git a/web_src/js/webcomponents/index.js b/web_src/js/webcomponents/index.js
new file mode 100644
index 0000000000..7cec9da734
--- /dev/null
+++ b/web_src/js/webcomponents/index.js
@@ -0,0 +1,5 @@
+import './polyfills.js';
+import '@github/relative-time-element';
+import './origin-url.js';
+import './overflow-menu.js';
+import './absolute-date.js';
diff --git a/web_src/js/webcomponents/GiteaOriginUrl.js b/web_src/js/webcomponents/origin-url.js
similarity index 91%
rename from web_src/js/webcomponents/GiteaOriginUrl.js
rename to web_src/js/webcomponents/origin-url.js
index 6e6f84d739..09aa77f2c0 100644
--- a/web_src/js/webcomponents/GiteaOriginUrl.js
+++ b/web_src/js/webcomponents/origin-url.js
@@ -15,7 +15,7 @@ export function toOriginUrl(urlStr) {
return urlStr;
}
-window.customElements.define('gitea-origin-url', class extends HTMLElement {
+window.customElements.define('origin-url', class extends HTMLElement {
connectedCallback() {
this.textContent = toOriginUrl(this.getAttribute('data-url'));
}
diff --git a/web_src/js/webcomponents/GiteaOriginUrl.test.js b/web_src/js/webcomponents/origin-url.test.js
similarity index 94%
rename from web_src/js/webcomponents/GiteaOriginUrl.test.js
rename to web_src/js/webcomponents/origin-url.test.js
index f0629842b8..3b2ab89f2a 100644
--- a/web_src/js/webcomponents/GiteaOriginUrl.test.js
+++ b/web_src/js/webcomponents/origin-url.test.js
@@ -1,4 +1,4 @@
-import {toOriginUrl} from './GiteaOriginUrl.js';
+import {toOriginUrl} from './origin-url.js';
test('toOriginUrl', () => {
const oldLocation = window.location;
diff --git a/web_src/js/webcomponents/overflow-menu.js b/web_src/js/webcomponents/overflow-menu.js
new file mode 100644
index 0000000000..9fa4585567
--- /dev/null
+++ b/web_src/js/webcomponents/overflow-menu.js
@@ -0,0 +1,179 @@
+import {throttle} from 'throttle-debounce';
+import {createTippy} from '../modules/tippy.js';
+import {isDocumentFragmentOrElementNode} from '../utils/dom.js';
+import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg';
+
+window.customElements.define('overflow-menu', class extends HTMLElement {
+ updateItems = throttle(100, () => {
+ if (!this.tippyContent) {
+ const div = document.createElement('div');
+ div.classList.add('tippy-target');
+ div.tabIndex = '-1'; // for initial focus, programmatic focus only
+ div.addEventListener('keydown', (e) => {
+ if (e.key === 'Tab') {
+ const items = this.tippyContent.querySelectorAll('[role="menuitem"]');
+ if (e.shiftKey) {
+ if (document.activeElement === items[0]) {
+ e.preventDefault();
+ items[items.length - 1].focus();
+ }
+ } else {
+ if (document.activeElement === items[items.length - 1]) {
+ e.preventDefault();
+ items[0].focus();
+ }
+ }
+ } else if (e.key === 'Escape') {
+ e.preventDefault();
+ e.stopPropagation();
+ this.button._tippy.hide();
+ this.button.focus();
+ } else if (e.key === ' ' || e.code === 'Enter') {
+ if (document.activeElement?.matches('[role="menuitem"]')) {
+ e.preventDefault();
+ e.stopPropagation();
+ document.activeElement.click();
+ }
+ } else if (e.key === 'ArrowDown') {
+ if (document.activeElement?.matches('.tippy-target')) {
+ e.preventDefault();
+ e.stopPropagation();
+ document.activeElement.querySelector('[role="menuitem"]:first-of-type').focus();
+ } else if (document.activeElement?.matches('[role="menuitem"]')) {
+ e.preventDefault();
+ e.stopPropagation();
+ document.activeElement.nextElementSibling?.focus();
+ }
+ } else if (e.key === 'ArrowUp') {
+ if (document.activeElement?.matches('.tippy-target')) {
+ e.preventDefault();
+ e.stopPropagation();
+ document.activeElement.querySelector('[role="menuitem"]:last-of-type').focus();
+ } else if (document.activeElement?.matches('[role="menuitem"]')) {
+ e.preventDefault();
+ e.stopPropagation();
+ document.activeElement.previousElementSibling?.focus();
+ }
+ }
+ });
+ this.append(div);
+ this.tippyContent = div;
+ }
+
+ // move items in tippy back into the menu items for subsequent measurement
+ for (const item of this.tippyItems || []) {
+ this.menuItemsEl.append(item);
+ }
+
+ // measure which items are partially outside the element and move them into the button menu
+ this.tippyItems = [];
+ const menuRight = this.offsetLeft + this.offsetWidth;
+ const menuItems = this.menuItemsEl.querySelectorAll('.item');
+ for (const item of menuItems) {
+ const itemRight = item.offsetLeft + item.offsetWidth;
+ if (menuRight - itemRight < 38) { // roughly the width of .overflow-menu-button
+ this.tippyItems.push(item);
+ }
+ }
+
+ // if there are no overflown items, remove any previously created button
+ if (!this.tippyItems?.length) {
+ const btn = this.querySelector('.overflow-menu-button');
+ btn?._tippy?.destroy();
+ btn?.remove();
+ return;
+ }
+
+ // remove aria role from items that moved from tippy to menu
+ for (const item of menuItems) {
+ if (!this.tippyItems.includes(item)) {
+ item.removeAttribute('role');
+ }
+ }
+
+ // move all items that overflow into tippy
+ for (const item of this.tippyItems) {
+ item.setAttribute('role', 'menuitem');
+ this.tippyContent.append(item);
+ }
+
+ // update existing tippy
+ if (this.button?._tippy) {
+ this.button._tippy.setContent(this.tippyContent);
+ return;
+ }
+
+ // create button initially
+ const btn = document.createElement('button');
+ btn.classList.add('overflow-menu-button', 'btn', 'tw-px-2', 'hover:tw-text-text-dark');
+ btn.setAttribute('aria-label', window.config.i18n.more_items);
+ btn.innerHTML = octiconKebabHorizontal;
+ this.append(btn);
+ this.button = btn;
+
+ createTippy(btn, {
+ trigger: 'click',
+ hideOnClick: true,
+ interactive: true,
+ placement: 'bottom-end',
+ role: 'menu',
+ content: this.tippyContent,
+ onShow: () => { // FIXME: onShown doesn't work (never be called)
+ setTimeout(() => {
+ this.tippyContent.focus();
+ }, 0);
+ },
+ });
+ });
+
+ init() {
+ // ResizeObserver triggers on initial render, so we don't manually call `updateItems` here which
+ // also avoids a full-page FOUC in Firefox that happens when `updateItems` is called too soon.
+ this.resizeObserver = new ResizeObserver((entries) => {
+ for (const entry of entries) {
+ const newWidth = entry.contentBoxSize[0].inlineSize;
+ if (newWidth !== this.lastWidth) {
+ requestAnimationFrame(() => {
+ this.updateItems();
+ });
+ this.lastWidth = newWidth;
+ }
+ }
+ });
+ this.resizeObserver.observe(this);
+ }
+
+ connectedCallback() {
+ this.setAttribute('role', 'navigation');
+
+ // check whether the mandatory `.overflow-menu-items` element is present initially which happens
+ // with Vue which renders differently than browsers. If it's not there, like in the case of browser
+ // template rendering, wait for its addition.
+ // The eslint rule is not sophisticated enough or aware of this problem, see
+ // https://github.com/43081j/eslint-plugin-wc/pull/130
+ const menuItemsEl = this.querySelector('.overflow-menu-items'); // eslint-disable-line wc/no-child-traversal-in-connectedcallback
+ if (menuItemsEl) {
+ this.menuItemsEl = menuItemsEl;
+ this.init();
+ } else {
+ this.mutationObserver = new MutationObserver((mutations) => {
+ for (const mutation of mutations) {
+ for (const node of mutation.addedNodes) {
+ if (!isDocumentFragmentOrElementNode(node)) continue;
+ if (node.classList.contains('overflow-menu-items')) {
+ this.menuItemsEl = node;
+ this.mutationObserver?.disconnect();
+ this.init();
+ }
+ }
+ }
+ });
+ this.mutationObserver.observe(this, {childList: true});
+ }
+ }
+
+ disconnectedCallback() {
+ this.mutationObserver?.disconnect();
+ this.resizeObserver?.disconnect();
+ }
+});
diff --git a/web_src/js/webcomponents/polyfill.js b/web_src/js/webcomponents/polyfills.js
similarity index 100%
rename from web_src/js/webcomponents/polyfill.js
rename to web_src/js/webcomponents/polyfills.js
diff --git a/web_src/js/webcomponents/webcomponents.js b/web_src/js/webcomponents/webcomponents.js
deleted file mode 100644
index 03348d895f..0000000000
--- a/web_src/js/webcomponents/webcomponents.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@webcomponents/custom-elements'; // polyfill for some browsers like PaleMoon
-import './polyfill.js';
-
-import '@github/relative-time-element';
-import './GiteaOriginUrl.js';
-import './GiteaAbsoluteDate.js';
diff --git a/webpack.config.js b/webpack.config.js
index a053771d63..883de8bef1 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -43,6 +43,18 @@ if ('ENABLE_SOURCEMAP' in env) {
sourceMaps = isProduction ? 'reduced' : 'true';
}
+// define which web components we use for Vue to not interpret them as Vue components
+const webComponents = new Set([
+ // our own, in web_src/js/webcomponents
+ 'overflow-menu',
+ 'origin-url',
+ 'absolute-date',
+ // from dependencies
+ 'markdown-toolbar',
+ 'relative-time',
+ 'text-expander',
+]);
+
const filterCssImport = (url, ...args) => {
const cssFile = args[1] || args[0]; // resourcePath is 2nd argument for url and 3rd for import
const importedFile = url.replace(/[?#].+/, '').toLowerCase();
@@ -72,7 +84,7 @@ export default {
fileURLToPath(new URL('web_src/css/index.css', import.meta.url)),
],
webcomponents: [
- fileURLToPath(new URL('web_src/js/webcomponents/webcomponents.js', import.meta.url)),
+ fileURLToPath(new URL('web_src/js/webcomponents/index.js', import.meta.url)),
],
forgejoswagger: [ // Forgejo swagger is OpenAPI 3.0.0 and has specific parameters
fileURLToPath(new URL('web_src/js/standalone/forgejo-swagger.js', import.meta.url)),
@@ -125,6 +137,11 @@ export default {
test: /\.vue$/i,
exclude: /node_modules/,
loader: 'vue-loader',
+ options: {
+ compilerOptions: {
+ isCustomElement: (tag) => webComponents.has(tag),
+ },
+ },
},
{
test: /\.js$/i,