mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-07-23 19:00:02 +02:00
Compare commits
4 commits
ae752e5446
...
f39181c4cb
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f39181c4cb | ||
![]() |
ccc33dd2df | ||
![]() |
8e4f50a909 | ||
![]() |
199d9cf65c |
15 changed files with 145 additions and 1943 deletions
1
Makefile
1
Makefile
|
@ -940,6 +940,7 @@ fomantic:
|
||||||
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
||||||
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
||||||
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
||||||
|
rm -rf $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/themes/default/modules/dropdown.overrides
|
||||||
$(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
|
$(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
|
||||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||||
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
|
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"docker:pinDigests",
|
"docker:pinDigests",
|
||||||
"helpers:pinGitHubActionDigests"
|
"helpers:pinGitHubActionDigests"
|
||||||
],
|
],
|
||||||
"baseBranches": [
|
"baseBranchPatterns": [
|
||||||
"$default",
|
"$default",
|
||||||
"/^v11\\.\\d+/forgejo$/",
|
"/^v11\\.\\d+/forgejo$/",
|
||||||
"/^v12\\.\\d+/forgejo$/"
|
"/^v12\\.\\d+/forgejo$/"
|
||||||
|
|
66
tests/e2e/image-diff.test.e2e.ts
Normal file
66
tests/e2e/image-diff.test.e2e.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// @watch start
|
||||||
|
// templates/repo/diff/**
|
||||||
|
// web_src/css/features/imagediff.css
|
||||||
|
// web_src/css/modules/tab.css
|
||||||
|
// web_src/js/modules/tab.ts
|
||||||
|
// @watch end
|
||||||
|
|
||||||
|
import {expect} from '@playwright/test';
|
||||||
|
import {save_visual, test, dynamic_id} from './utils_e2e.ts';
|
||||||
|
|
||||||
|
test.use({user: 'user2'});
|
||||||
|
|
||||||
|
test('Repository image diff', async ({page}) => {
|
||||||
|
// Generate a temporary SVG and edit it.
|
||||||
|
let response = await page.goto('/user2/repo1/_new/master', {waitUntil: 'domcontentloaded'});
|
||||||
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
const filename = `${dynamic_id()}.svg`;
|
||||||
|
|
||||||
|
await page.getByPlaceholder('Name your file…').fill(filename);
|
||||||
|
await page.locator('.monaco-editor').click();
|
||||||
|
await page.keyboard.type('<svg version="1.1" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><circle cx="150" cy="100" r="80" fill="green" /></svg>\n');
|
||||||
|
|
||||||
|
await page.locator('.quick-pull-choice input[value="direct"]').click();
|
||||||
|
await page.getByRole('button', {name: 'Commit changes'}).click();
|
||||||
|
|
||||||
|
response = await page.goto(`/user2/repo1/_edit/master/${filename}`, {waitUntil: 'domcontentloaded'});
|
||||||
|
expect(response?.status()).toBe(200);
|
||||||
|
|
||||||
|
await page.locator('.monaco-editor').click();
|
||||||
|
await page.keyboard.press('Meta+KeyA');
|
||||||
|
await page.keyboard.type('<svg version="1.1" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><circle cx="150" cy="100" r="80" fill="red" /></svg>\n');
|
||||||
|
|
||||||
|
await page.locator('.quick-pull-choice input[value="direct"]').click();
|
||||||
|
await page.getByRole('button', {name: 'Commit changes'}).click();
|
||||||
|
|
||||||
|
// Go to the commit page, where a image diff is shown.
|
||||||
|
await page.locator('.commit-summary a.default-link').click();
|
||||||
|
|
||||||
|
// Exhaustively test tabs works as expected
|
||||||
|
await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).toContainClass('active');
|
||||||
|
await expect(page.locator('.item[data-tab="diff-swipe-1"]')).not.toContainClass('active');
|
||||||
|
await expect(page.locator('.item[data-tab="diff-overlay-1"]')).not.toContainClass('active');
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeVisible();
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeHidden();
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeHidden();
|
||||||
|
await save_visual(page);
|
||||||
|
|
||||||
|
await page.getByText('Swipe').click();
|
||||||
|
await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).not.toContainClass('active');
|
||||||
|
await expect(page.locator('.item[data-tab="diff-swipe-1"]')).toContainClass('active');
|
||||||
|
await expect(page.locator('.item[data-tab="diff-overlay-1"]')).not.toContainClass('active');
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeHidden();
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeVisible();
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeHidden();
|
||||||
|
await save_visual(page);
|
||||||
|
|
||||||
|
await page.getByText('Overlay').click();
|
||||||
|
await expect(page.locator('.item[data-tab="diff-side-by-side-1"]')).not.toContainClass('active');
|
||||||
|
await expect(page.locator('.item[data-tab="diff-swipe-1"]')).not.toContainClass('active');
|
||||||
|
await expect(page.locator('.item[data-tab="diff-overlay-1"]')).toContainClass('active');
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-side-by-side-1"]')).toBeHidden();
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-swipe-1"]')).toBeHidden();
|
||||||
|
await expect(page.locator('.tab[data-tab="diff-overlay-1"]')).toBeVisible();
|
||||||
|
await save_visual(page);
|
||||||
|
});
|
|
@ -1,4 +1,6 @@
|
||||||
// @watch start
|
// @watch start
|
||||||
|
// web_src/js/modules/tab.ts
|
||||||
|
// web_src/css/modules/tab.css
|
||||||
// web_src/js/features/comp/ComboMarkdownEditor.js
|
// web_src/js/features/comp/ComboMarkdownEditor.js
|
||||||
// web_src/css/editor/combomarkdowneditor.css
|
// web_src/css/editor/combomarkdowneditor.css
|
||||||
// templates/shared/combomarkdowneditor.tmpl
|
// templates/shared/combomarkdowneditor.tmpl
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
@import "./modules/segment.css";
|
@import "./modules/segment.css";
|
||||||
@import "./modules/grid.css";
|
@import "./modules/grid.css";
|
||||||
@import "./modules/message.css";
|
@import "./modules/message.css";
|
||||||
|
@import "./modules/tab.css";
|
||||||
@import "./modules/table.css";
|
@import "./modules/table.css";
|
||||||
@import "./modules/card.css";
|
@import "./modules/card.css";
|
||||||
@import "./modules/checkbox.css";
|
@import "./modules/checkbox.css";
|
||||||
|
|
7
web_src/css/modules/tab.css
Normal file
7
web_src/css/modules/tab.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.ui.tab {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.tab.active {
|
||||||
|
display: block;
|
||||||
|
}
|
|
@ -14,9 +14,14 @@
|
||||||
@variationButtonAnimated: false;
|
@variationButtonAnimated: false;
|
||||||
@variationButtonAnimatedFade: false;
|
@variationButtonAnimatedFade: false;
|
||||||
@variationButtonAttached: false;
|
@variationButtonAttached: false;
|
||||||
|
@variationButtonCircular: false;
|
||||||
|
@variationButtonFloated: false;
|
||||||
@variationButtonInverted: false;
|
@variationButtonInverted: false;
|
||||||
|
@variationButtonToggle: false;
|
||||||
@variationButtonSocial: false;
|
@variationButtonSocial: false;
|
||||||
@variationButtonTertiary: false;
|
@variationButtonTertiary: false;
|
||||||
|
@variationButtonOr: false;
|
||||||
|
@variationButtonSizes: mini, tiny, small;
|
||||||
@variationCalendarInverted: false;
|
@variationCalendarInverted: false;
|
||||||
@variationCardInverted: false;
|
@variationCardInverted: false;
|
||||||
@variationCheckboxInverted: false;
|
@variationCheckboxInverted: false;
|
||||||
|
@ -24,14 +29,18 @@
|
||||||
@variationDimmerInverted: false;
|
@variationDimmerInverted: false;
|
||||||
@variationDividerInverted: false;
|
@variationDividerInverted: false;
|
||||||
@variationDropdownInverted: false;
|
@variationDropdownInverted: false;
|
||||||
|
@variationDropdownScrollhint: false;
|
||||||
|
@variationDropdownSimple: false;
|
||||||
@variationFeedInverted: false;
|
@variationFeedInverted: false;
|
||||||
@variationFlyoutInverted: false;
|
@variationFlyoutInverted: false;
|
||||||
@variationFormInverted: false;
|
@variationFormInverted: false;
|
||||||
@variationFormTransparent: false;
|
@variationFormTransparent: false;
|
||||||
|
@variationFormSizes: false;
|
||||||
@variationGridDoubling: false;
|
@variationGridDoubling: false;
|
||||||
@variationGridInverted: false;
|
@variationGridInverted: false;
|
||||||
@variationHeaderInverted: false;
|
@variationHeaderInverted: false;
|
||||||
@variationIconInverted: false;
|
@variationIconInverted: false;
|
||||||
|
@variationInputAction: false;
|
||||||
@variationInputInverted: false;
|
@variationInputInverted: false;
|
||||||
@variationItemInverted: false;
|
@variationItemInverted: false;
|
||||||
@variationLabelCorner: false;
|
@variationLabelCorner: false;
|
||||||
|
@ -40,14 +49,25 @@
|
||||||
@variationLabelRibbon: false;
|
@variationLabelRibbon: false;
|
||||||
@variationLabelTag: false;
|
@variationLabelTag: false;
|
||||||
@variationListInverted: false;
|
@variationListInverted: false;
|
||||||
|
@variationMenuText: false;
|
||||||
|
@variationMenuFitted: false;
|
||||||
|
@variationMenuFixed: false;
|
||||||
@variationMenuInverted: false;
|
@variationMenuInverted: false;
|
||||||
@variationMessageInverted: false;
|
@variationMessageInverted: false;
|
||||||
|
@variationModalBasic: false;
|
||||||
@variationModalInverted: false;
|
@variationModalInverted: false;
|
||||||
|
@variationModalLegacy: false;
|
||||||
|
@variationModalOverlay: false;
|
||||||
|
@variationModalSizes: mini, tiny, small;
|
||||||
@variationNagInverted: false;
|
@variationNagInverted: false;
|
||||||
@variationPlaceholderInverted: false;
|
@variationPlaceholderInverted: false;
|
||||||
@variationPopupInverted: false;
|
@variationPopupInverted: false;
|
||||||
@variationPopupTooltip: false;
|
@variationPopupTooltip: false;
|
||||||
@variationProgressInverted: false;
|
@variationProgressInverted: false;
|
||||||
|
@variationSearchAligned: false;
|
||||||
|
@variationSearchCategory: false;
|
||||||
|
@variationSearchShort: false;
|
||||||
|
@variationSearchLong: false;
|
||||||
@variationSegmentInverted: false;
|
@variationSegmentInverted: false;
|
||||||
@variationSegmentPiled: false;
|
@variationSegmentPiled: false;
|
||||||
@variationSegmentStacked: false;
|
@variationSegmentStacked: false;
|
||||||
|
|
931
web_src/fomantic/build/semantic.css
generated
931
web_src/fomantic/build/semantic.css
generated
File diff suppressed because it is too large
Load diff
1002
web_src/fomantic/build/semantic.js
generated
1002
web_src/fomantic/build/semantic.js
generated
File diff suppressed because it is too large
Load diff
|
@ -27,7 +27,6 @@
|
||||||
"form",
|
"form",
|
||||||
"menu",
|
"menu",
|
||||||
"modal",
|
"modal",
|
||||||
"search",
|
"search"
|
||||||
"tab"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {confirmModal} from './comp/ConfirmModal.js';
|
||||||
import {showErrorToast} from '../modules/toast.js';
|
import {showErrorToast} from '../modules/toast.js';
|
||||||
import {request, POST, GET} from '../modules/fetch.js';
|
import {request, POST, GET} from '../modules/fetch.js';
|
||||||
import '../htmx.js';
|
import '../htmx.js';
|
||||||
|
import {initTab} from '../modules/tab.ts';
|
||||||
|
|
||||||
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
|
const {appUrl, appSubUrl, csrfToken, i18n} = window.config;
|
||||||
|
|
||||||
|
@ -195,7 +196,9 @@ export function initGlobalCommon() {
|
||||||
$uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
|
$uiDropdowns.filter('.upward').dropdown('setting', 'direction', 'upward');
|
||||||
$uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward');
|
$uiDropdowns.filter('.downward').dropdown('setting', 'direction', 'downward');
|
||||||
|
|
||||||
$('.tabular.menu .item').tab();
|
for (const el of document.querySelectorAll('.tabular.menu')) {
|
||||||
|
initTab(el);
|
||||||
|
}
|
||||||
|
|
||||||
initSubmitEventPolyfill();
|
initSubmitEventPolyfill();
|
||||||
document.addEventListener('submit', formFetchAction);
|
document.addEventListener('submit', formFetchAction);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {easyMDEToolbarActions} from './EasyMDEToolbarActions.js';
|
||||||
import {initTextExpander} from './TextExpander.js';
|
import {initTextExpander} from './TextExpander.js';
|
||||||
import {showErrorToast, showHintToast} from '../../modules/toast.js';
|
import {showErrorToast, showHintToast} from '../../modules/toast.js';
|
||||||
import {POST} from '../../modules/fetch.js';
|
import {POST} from '../../modules/fetch.js';
|
||||||
|
import {initTab} from '../../modules/tab.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* validate if the given textarea is non-empty.
|
* validate if the given textarea is non-empty.
|
||||||
|
@ -200,7 +201,8 @@ class ComboMarkdownEditor {
|
||||||
|
|
||||||
setupTab() {
|
setupTab() {
|
||||||
const $container = $(this.container);
|
const $container = $(this.container);
|
||||||
const tabs = $container[0].querySelectorAll('.switch > .item');
|
const switchEl = $container[0].querySelector('.switch');
|
||||||
|
const tabs = switchEl.querySelectorAll('.item');
|
||||||
|
|
||||||
// Fomantic Tab requires the "data-tab" to be globally unique.
|
// Fomantic Tab requires the "data-tab" to be globally unique.
|
||||||
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
|
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
|
||||||
|
@ -221,7 +223,7 @@ class ComboMarkdownEditor {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(tabs).tab();
|
initTab(switchEl);
|
||||||
|
|
||||||
this.previewUrl = tabPreviewer.getAttribute('data-preview-url');
|
this.previewUrl = tabPreviewer.getAttribute('data-preview-url');
|
||||||
this.previewContext = tabPreviewer.getAttribute('data-preview-context');
|
this.previewContext = tabPreviewer.getAttribute('data-preview-context');
|
||||||
|
|
|
@ -5,10 +5,11 @@ import {hideElem, showElem, createElementFromHTML} from '../utils/dom.js';
|
||||||
import {initMarkupContent} from '../markup/content.js';
|
import {initMarkupContent} from '../markup/content.js';
|
||||||
import {attachRefIssueContextPopup} from './contextpopup.js';
|
import {attachRefIssueContextPopup} from './contextpopup.js';
|
||||||
import {POST} from '../modules/fetch.js';
|
import {POST} from '../modules/fetch.js';
|
||||||
|
import {initTab} from '../modules/tab.ts';
|
||||||
|
|
||||||
function initEditPreviewTab($form) {
|
function initEditPreviewTab($form) {
|
||||||
const $tabMenu = $form.find('.tabular.menu');
|
const $tabMenu = $form.find('.tabular.menu');
|
||||||
$tabMenu.find('.item').tab();
|
initTab($tabMenu[0]);
|
||||||
const $previewTab = $tabMenu.find(
|
const $previewTab = $tabMenu.find(
|
||||||
`.item[data-tab="${$tabMenu.data('preview')}"]`,
|
`.item[data-tab="${$tabMenu.data('preview')}"]`,
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,9 +11,6 @@ import {initDimmer} from './dimmer.ts';
|
||||||
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
|
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
|
||||||
|
|
||||||
export function initGiteaFomantic() {
|
export function initGiteaFomantic() {
|
||||||
// Silence fomantic's error logging when tabs are used without a target content element
|
|
||||||
$.fn.tab.settings.silent = true;
|
|
||||||
|
|
||||||
// By default, use "exact match" for full text search
|
// By default, use "exact match" for full text search
|
||||||
$.fn.dropdown.settings.fullTextSearch = 'exact';
|
$.fn.dropdown.settings.fullTextSearch = 'exact';
|
||||||
// Do not use "cursor: pointer" for dropdown labels
|
// Do not use "cursor: pointer" for dropdown labels
|
||||||
|
|
36
web_src/js/modules/tab.ts
Normal file
36
web_src/js/modules/tab.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
export function initTab(parentEl: Element) {
|
||||||
|
if (!parentEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of which tab is active for this element.
|
||||||
|
let activeTabPath = parentEl.querySelector('.item.active')?.getAttribute('data-tab');
|
||||||
|
if (!activeTabPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const el of parentEl.querySelectorAll('.item')) {
|
||||||
|
el.addEventListener('click', (ev) => {
|
||||||
|
// There's no data-tab attribute we can't do anything, ignore.
|
||||||
|
const tabPath = el.getAttribute('data-tab');
|
||||||
|
if (!tabPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The item is already active, ignore.
|
||||||
|
if (el.classList.contains('active')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the current item active and the previous item inactive.
|
||||||
|
parentEl.querySelector('.item.active').classList.remove('active');
|
||||||
|
document.querySelector(`.tab.active[data-tab=${activeTabPath}]`).classList.remove('active');
|
||||||
|
el.classList.add('active');
|
||||||
|
document.querySelector(`.tab[data-tab=${tabPath}]`).classList.add('active');
|
||||||
|
activeTabPath = tabPath;
|
||||||
|
|
||||||
|
// Not really sure if this is useful, it is kept from how Fomantic did it.
|
||||||
|
ev.preventDefault();
|
||||||
|
}, {passive: false});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue