diff options
Diffstat (limited to 'web_src/js')
-rw-r--r-- | web_src/js/components/RepoActivityTopAuthors.vue | 4 | ||||
-rw-r--r-- | web_src/js/features/repo-issue.ts | 7 | ||||
-rw-r--r-- | web_src/js/features/repo-migration.ts | 8 | ||||
-rw-r--r-- | web_src/js/modules/toast.ts | 2 | ||||
-rw-r--r-- | web_src/js/render/plugins/3d-viewer.ts | 1 | ||||
-rw-r--r-- | web_src/js/utils.ts | 28 | ||||
-rw-r--r-- | web_src/js/utils/color.ts | 7 | ||||
-rw-r--r-- | web_src/js/utils/dom.ts | 20 | ||||
-rw-r--r-- | web_src/js/utils/image.ts | 4 | ||||
-rw-r--r-- | web_src/js/utils/time.ts | 4 | ||||
-rw-r--r-- | web_src/js/utils/url.ts | 4 |
11 files changed, 49 insertions, 40 deletions
diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue index bbdfda41d0..5a925f9943 100644 --- a/web_src/js/components/RepoActivityTopAuthors.vue +++ b/web_src/js/components/RepoActivityTopAuthors.vue @@ -58,8 +58,8 @@ onMounted(() => { <template> <div> - <div class="activity-bar-graph" ref="styleElement" style="width: 0; height: 0;"/> - <div class="activity-bar-graph-alt" ref="altStyleElement" style="width: 0; height: 0;"/> + <div class="activity-bar-graph tw-w-0 tw-h-0" ref="styleElement"/> + <div class="activity-bar-graph-alt tw-w-0 tw-h-0" ref="altStyleElement"/> <vue-bar-graph :points="graphPoints" :show-x-axis="true" diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 49e8fc40a2..b330b4869b 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -542,7 +542,12 @@ function initIssueTemplateCommentEditors(commentForm: HTMLFormElement) { // deactivate all markdown editors showElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-real')); hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .combo-markdown-editor')); - hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-dropzone')); + queryElems(commentForm, '.combo-editor-dropzone .form-field-dropzone', (dropzoneContainer) => { + // if "form-field-dropzone" exists, then "dropzone" must also exist + const dropzone = dropzoneContainer.querySelector<HTMLElement>('.dropzone').dropzone; + const hasUploadedFiles = dropzone.files.length !== 0; + toggleElem(dropzoneContainer, hasUploadedFiles); + }); // activate this markdown editor hideElem(fieldTextarea); diff --git a/web_src/js/features/repo-migration.ts b/web_src/js/features/repo-migration.ts index 4914e47267..0b348b45fb 100644 --- a/web_src/js/features/repo-migration.ts +++ b/web_src/js/features/repo-migration.ts @@ -34,8 +34,12 @@ export function initRepoMigration() { elCloneAddr.addEventListener('input', () => { if (repoNameChanged) return; let repoNameFromUrl = elCloneAddr.value.split(/[?#]/)[0]; - repoNameFromUrl = /^(.*\/)?((.+?)\/?)$/.exec(repoNameFromUrl)[3]; - repoNameFromUrl = repoNameFromUrl.split(/[?#]/)[0]; + const parts = /^(.*\/)?((.+?)\/?)$/.exec(repoNameFromUrl); + if (!parts || parts.length < 4) { + elRepoName.value = ''; + return; + } + repoNameFromUrl = parts[3].split(/[?#]/)[0]; elRepoName.value = sanitizeRepoName(repoNameFromUrl); }); } diff --git a/web_src/js/modules/toast.ts b/web_src/js/modules/toast.ts index 087103cbd8..c28fe746b0 100644 --- a/web_src/js/modules/toast.ts +++ b/web_src/js/modules/toast.ts @@ -42,7 +42,7 @@ type ToastOpts = { type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast }; -// See https://github.com/apvarun/toastify-js#api for options +/** See https://github.com/apvarun/toastify-js#api for options */ function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast { const body = useHtmlBody ? message : htmlEscape(message); const parent = document.querySelector('.ui.dimmer.active') ?? document.body; diff --git a/web_src/js/render/plugins/3d-viewer.ts b/web_src/js/render/plugins/3d-viewer.ts index 2a0929359d..6f3ee15d26 100644 --- a/web_src/js/render/plugins/3d-viewer.ts +++ b/web_src/js/render/plugins/3d-viewer.ts @@ -3,7 +3,6 @@ import {extname} from '../../utils.ts'; // support common 3D model file formats, use online-3d-viewer library for rendering -// eslint-disable-next-line multiline-comment-style /* a simple text STL file example: solid SimpleTriangle facet normal 0 0 1 diff --git a/web_src/js/utils.ts b/web_src/js/utils.ts index f396a8e4f6..5e2f4d5106 100644 --- a/web_src/js/utils.ts +++ b/web_src/js/utils.ts @@ -2,19 +2,19 @@ import {decode, encode} from 'uint8-to-base64'; import type {IssuePageInfo, IssuePathInfo, RepoOwnerPathInfo} from './types.ts'; import {toggleElemClass, toggleElem} from './utils/dom.ts'; -// transform /path/to/file.ext to /path/to +/** transform /path/to/file.ext to /path/to */ export function dirname(path: string): string { const lastSlashIndex = path.lastIndexOf('/'); return lastSlashIndex < 0 ? '' : path.substring(0, lastSlashIndex); } -// transform /path/to/file.ext to file.ext +/** transform /path/to/file.ext to file.ext */ export function basename(path: string): string { const lastSlashIndex = path.lastIndexOf('/'); return lastSlashIndex < 0 ? path : path.substring(lastSlashIndex + 1); } -// transform /path/to/file.ext to .ext +/** transform /path/to/file.ext to .ext */ export function extname(path: string): string { const lastSlashIndex = path.lastIndexOf('/'); const lastPointIndex = path.lastIndexOf('.'); @@ -22,18 +22,18 @@ export function extname(path: string): string { return lastPointIndex < 0 ? '' : path.substring(lastPointIndex); } -// test whether a variable is an object +/** test whether a variable is an object */ export function isObject(obj: any): boolean { return Object.prototype.toString.call(obj) === '[object Object]'; } -// returns whether a dark theme is enabled +/** returns whether a dark theme is enabled */ export function isDarkTheme(): boolean { const style = window.getComputedStyle(document.documentElement); return style.getPropertyValue('--is-dark-theme').trim().toLowerCase() === 'true'; } -// strip <tags> from a string +/** strip <tags> from a string */ export function stripTags(text: string): string { return text.replace(/<[^>]*>?/g, ''); } @@ -62,27 +62,27 @@ export function parseIssuePageInfo(): IssuePageInfo { }; } -// parse a URL, either relative '/path' or absolute 'https://localhost/path' +/** parse a URL, either relative '/path' or absolute 'https://localhost/path' */ export function parseUrl(str: string): URL { return new URL(str, str.startsWith('http') ? undefined : window.location.origin); } -// return current locale chosen by user +/** return current locale chosen by user */ export function getCurrentLocale(): string { return document.documentElement.lang; } -// given a month (0-11), returns it in the documents language +/** given a month (0-11), returns it in the documents language */ export function translateMonth(month: number) { return new Date(Date.UTC(2022, month, 12)).toLocaleString(getCurrentLocale(), {month: 'short', timeZone: 'UTC'}); } -// given a weekday (0-6, Sunday to Saturday), returns it in the documents language +/** given a weekday (0-6, Sunday to Saturday), returns it in the documents language */ export function translateDay(day: number) { return new Date(Date.UTC(2022, 7, day)).toLocaleString(getCurrentLocale(), {weekday: 'short', timeZone: 'UTC'}); } -// convert a Blob to a DataURI +/** convert a Blob to a DataURI */ export function blobToDataURI(blob: Blob): Promise<string> { return new Promise((resolve, reject) => { try { @@ -100,7 +100,7 @@ export function blobToDataURI(blob: Blob): Promise<string> { }); } -// convert image Blob to another mime-type format. +/** convert image Blob to another mime-type format. */ export function convertImage(blob: Blob, mime: string): Promise<Blob> { return new Promise(async (resolve, reject) => { try { @@ -143,7 +143,7 @@ export function toAbsoluteUrl(url: string): string { return `${window.location.origin}${url}`; } -// Encode an Uint8Array into a URLEncoded base64 string. +/** Encode an Uint8Array into a URLEncoded base64 string. */ export function encodeURLEncodedBase64(uint8Array: Uint8Array): string { return encode(uint8Array) .replace(/\+/g, '-') @@ -151,7 +151,7 @@ export function encodeURLEncodedBase64(uint8Array: Uint8Array): string { .replace(/=/g, ''); } -// Decode a URLEncoded base64 to an Uint8Array. +/** Decode a URLEncoded base64 to an Uint8Array. */ export function decodeURLEncodedBase64(base64url: string): Uint8Array { return decode(base64url .replace(/_/g, '/') diff --git a/web_src/js/utils/color.ts b/web_src/js/utils/color.ts index a0409353d2..57c909b8a0 100644 --- a/web_src/js/utils/color.ts +++ b/web_src/js/utils/color.ts @@ -1,7 +1,7 @@ import tinycolor from 'tinycolor2'; import type {ColorInput} from 'tinycolor2'; -// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance +/** Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance */ // Keep this in sync with modules/util/color.go function getRelativeLuminance(color: ColorInput): number { const {r, g, b} = tinycolor(color).toRgb(); @@ -12,8 +12,9 @@ function useLightText(backgroundColor: ColorInput): boolean { return getRelativeLuminance(backgroundColor) < 0.453; } -// Given a background color, returns a black or white foreground color that the highest -// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better. +/** Given a background color, returns a black or white foreground color that the highest + * contrast ratio. */ +// In the future, the APCA contrast function, or CSS `contrast-color` will be better. // https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42 export function contrastColor(backgroundColor: ColorInput): string { return useLightText(backgroundColor) ? '#fff' : '#000'; diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts index 6d6a3735da..8b7219c678 100644 --- a/web_src/js/utils/dom.ts +++ b/web_src/js/utils/dom.ts @@ -71,7 +71,7 @@ export function queryElemSiblings<T extends Element>(el: Element, selector = '*' }), fn); } -// it works like jQuery.children: only the direct children are selected +/** it works like jQuery.children: only the direct children are selected */ export function queryElemChildren<T extends Element>(parent: Element | ParentNode, selector = '*', fn?: ElementsCallback<T>): ArrayLikeIterable<T> { if (isInFrontendUnitTest()) { // https://github.com/capricorn86/happy-dom/issues/1620 : ":scope" doesn't work @@ -81,7 +81,7 @@ export function queryElemChildren<T extends Element>(parent: Element | ParentNod return applyElemsCallback<T>(parent.querySelectorAll(`:scope > ${selector}`), fn); } -// it works like parent.querySelectorAll: all descendants are selected +/** it works like parent.querySelectorAll: all descendants are selected */ // in the future, all "queryElems(document, ...)" should be refactored to use a more specific parent if the targets are not for page-level components. export function queryElems<T extends HTMLElement>(parent: Element | ParentNode, selector: string, fn?: ElementsCallback<T>): ArrayLikeIterable<T> { return applyElemsCallback<T>(parent.querySelectorAll(selector), fn); @@ -95,8 +95,8 @@ export function onDomReady(cb: () => Promisable<void>) { } } -// checks whether an element is owned by the current document, and whether it is a document fragment or element node -// if it is, it means it is a "normal" element managed by us, which can be modified safely. +/** checks whether an element is owned by the current document, and whether it is a document fragment or element node + * if it is, it means it is a "normal" element managed by us, which can be modified safely. */ export function isDocumentFragmentOrElementNode(el: Node) { try { return el.ownerDocument === document && el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.DOCUMENT_FRAGMENT_NODE; @@ -106,8 +106,8 @@ export function isDocumentFragmentOrElementNode(el: Node) { } } -// autosize a textarea to fit content. Based on -// https://github.com/github/textarea-autosize +/** autosize a textarea to fit content. */ +// Based on https://github.com/github/textarea-autosize // --------------------------------------------------------------------- // Copyright (c) 2018 GitHub, Inc. // @@ -246,8 +246,8 @@ export function onInputDebounce(fn: () => Promisable<any>) { type LoadableElement = HTMLEmbedElement | HTMLIFrameElement | HTMLImageElement | HTMLScriptElement | HTMLTrackElement; -// Set the `src` attribute on an element and returns a promise that resolves once the element -// has loaded or errored. +/** Set the `src` attribute on an element and returns a promise that resolves once the element + * has loaded or errored. */ export function loadElem(el: LoadableElement, src: string) { return new Promise((resolve) => { el.addEventListener('load', () => resolve(true), {once: true}); @@ -286,7 +286,7 @@ export function isElemVisible(el: HTMLElement): boolean { return !el.classList.contains('tw-hidden') && (el.offsetWidth || el.offsetHeight || el.getClientRects().length) && el.style.display !== 'none'; } -// replace selected text in a textarea while preserving editor history, e.g. CTRL-Z works after this +/** replace selected text in a textarea while preserving editor history, e.g. CTRL-Z works after this */ export function replaceTextareaSelection(textarea: HTMLTextAreaElement, text: string) { const before = textarea.value.slice(0, textarea.selectionStart ?? undefined); const after = textarea.value.slice(textarea.selectionEnd ?? undefined); @@ -368,7 +368,7 @@ export function addDelegatedEventListener<T extends HTMLElement, E extends Event }, options); } -// Returns whether a click event is a left-click without any modifiers held +/** Returns whether a click event is a left-click without any modifiers held */ export function isPlainClick(e: MouseEvent) { return e.button === 0 && !e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey; } diff --git a/web_src/js/utils/image.ts b/web_src/js/utils/image.ts index 558a63f22e..5cd5052b40 100644 --- a/web_src/js/utils/image.ts +++ b/web_src/js/utils/image.ts @@ -29,8 +29,8 @@ type ImageInfo = { dppx?: number, } -// decode a image and try to obtain width and dppx. It will never throw but instead -// return default values. +/** decode a image and try to obtain width and dppx. It will never throw but instead + * return default values. */ export async function imageInfo(blob: Blob): Promise<ImageInfo> { let width = 0, dppx = 1; // dppx: 1 dot per pixel for non-HiDPI screens diff --git a/web_src/js/utils/time.ts b/web_src/js/utils/time.ts index c63498345f..262cc23a52 100644 --- a/web_src/js/utils/time.ts +++ b/web_src/js/utils/time.ts @@ -65,8 +65,8 @@ export function fillEmptyStartDaysWithZeroes(startDays: number[], data: DayDataO let dateFormat: Intl.DateTimeFormat; -// format a Date object to document's locale, but with 24h format from user's current locale because this -// option is a personal preference of the user, not something that the document's locale should dictate. +/** Format a Date object to document's locale, but with 24h format from user's current locale because this + * option is a personal preference of the user, not something that the document's locale should dictate. */ export function formatDatetime(date: Date | number): string { if (!dateFormat) { // TODO: replace `hour12` with `Intl.Locale.prototype.getHourCycles` once there is broad browser support diff --git a/web_src/js/utils/url.ts b/web_src/js/utils/url.ts index a7d61c5e83..9991da7472 100644 --- a/web_src/js/utils/url.ts +++ b/web_src/js/utils/url.ts @@ -14,8 +14,8 @@ export function isUrl(url: string): boolean { } } -// Convert an absolute or relative URL to an absolute URL with the current origin. It only -// processes absolute HTTP/HTTPS URLs or relative URLs like '/xxx' or '//host/xxx'. +/** Convert an absolute or relative URL to an absolute URL with the current origin. It only + * processes absolute HTTP/HTTPS URLs or relative URLs like '/xxx' or '//host/xxx'. */ export function toOriginUrl(urlStr: string) { try { if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { |