diff options
author | HampusM <hampus@hampusmat.com> | 2021-07-06 13:07:07 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2021-07-06 13:07:07 +0200 |
commit | 39c38c737114dc7f946b1895b2a4ba27a5546c60 (patch) | |
tree | 61344a90155845d08c4811da16e6f90504da2625 /packages/client/src/components | |
parent | 4102adab527ccc4f6d8ac210118dea21177c212d (diff) |
Migrated frontend to typescript
Diffstat (limited to 'packages/client/src/components')
-rw-r--r-- | packages/client/src/components/BaseBackButton.vue | 8 | ||||
-rw-r--r-- | packages/client/src/components/BaseBreadcrumb.vue | 8 | ||||
-rw-r--r-- | packages/client/src/components/BaseButton.vue | 8 | ||||
-rw-r--r-- | packages/client/src/components/BaseErrorMessage.vue | 8 | ||||
-rw-r--r-- | packages/client/src/components/CommitPatch.vue | 248 | ||||
-rw-r--r-- | packages/client/src/components/HomeHeader.vue | 8 | ||||
-rw-r--r-- | packages/client/src/components/HomeProjectsHeader.vue | 10 | ||||
-rw-r--r-- | packages/client/src/components/RepositoryCloneDropdown.vue | 106 | ||||
-rw-r--r-- | packages/client/src/components/RepositoryHeader.vue | 10 | ||||
-rw-r--r-- | packages/client/src/components/RepositoryNavbar.vue | 10 | ||||
-rw-r--r-- | packages/client/src/components/RepositoryTreeBlob.vue | 39 | ||||
-rw-r--r-- | packages/client/src/components/RepositoryTreeTree.vue | 15 |
12 files changed, 274 insertions, 204 deletions
diff --git a/packages/client/src/components/BaseBackButton.vue b/packages/client/src/components/BaseBackButton.vue index 2de0c77..3139a6c 100644 --- a/packages/client/src/components/BaseBackButton.vue +++ b/packages/client/src/components/BaseBackButton.vue @@ -12,8 +12,10 @@ </div> </template> -<script> -export default { +<script lang="ts"> +import { defineComponent } from "vue"; + +export default defineComponent({ name: "BaseBackButton", props: { to: { @@ -21,7 +23,7 @@ export default { required: true } } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/BaseBreadcrumb.vue b/packages/client/src/components/BaseBreadcrumb.vue index 94d9956..45e835c 100644 --- a/packages/client/src/components/BaseBreadcrumb.vue +++ b/packages/client/src/components/BaseBreadcrumb.vue @@ -15,8 +15,10 @@ </nav> </template> -<script> -export default { +<script lang="ts"> +import { defineComponent } from "vue"; + +export default defineComponent({ name: "BaseBreadcrumb", props: { items: { @@ -28,7 +30,7 @@ export default { required: true } } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/BaseButton.vue b/packages/client/src/components/BaseButton.vue index 2a528b0..4dad973 100644 --- a/packages/client/src/components/BaseButton.vue +++ b/packages/client/src/components/BaseButton.vue @@ -4,8 +4,10 @@ </button> </template> -<script> -export default { +<script lang="ts"> +import { defineComponent } from "vue"; + +export default defineComponent({ name: "BaseButton", props: { type: { @@ -17,7 +19,7 @@ export default { required: true } } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/BaseErrorMessage.vue b/packages/client/src/components/BaseErrorMessage.vue index 7f193ce..e2c0bcf 100644 --- a/packages/client/src/components/BaseErrorMessage.vue +++ b/packages/client/src/components/BaseErrorMessage.vue @@ -5,8 +5,10 @@ </div> </template> -<script> -export default { +<script lang="ts"> +import { defineComponent } from "vue"; + +export default defineComponent({ name: "BaseErrorMessage", props: { fetchFailed: { @@ -14,7 +16,7 @@ export default { required: true } } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/CommitPatch.vue b/packages/client/src/components/CommitPatch.vue index fdeb28d..a51a6cb 100644 --- a/packages/client/src/components/CommitPatch.vue +++ b/packages/client/src/components/CommitPatch.vue @@ -1,129 +1,153 @@ -<script> -import { h } from "vue"; +<script lang="ts"> +import { defineComponent, h, PropType, VNode } from "vue"; import hljs from "highlight.js"; import hljs_languages from "../util/hljs-languages"; -export default { +type Hunk = { + new_start: number, + new_lines_cnt: number, + new_lines: number[], + old_start: number, + old_lines_cnt: number, + deleted_lines: number[], + hunk: string +} + +type Patch = { + additions: number, + deletions: number, + from: string, + to: string, + too_large: boolean, + hunks: Hunk[] +} + +export default defineComponent({ name: "CommitPatch", props: { patch: { - type: Object, + type: Object as PropType<Patch>, required: true } }, setup(props) { - let commit_patch; - - if(props.patch.too_large === false) { - let all_hunks = props.patch.hunks.map((hunk) => hunk.hunk.split("\n").slice(1).join("\n")); - - const language = hljs_languages.find((lang) => lang.extensions.some((extension) => props.patch.to.endsWith(extension))); - let highlighted = language ? hljs.highlight(all_hunks.join("\n"), { language: language.name }) : hljs.highlightAuto(all_hunks.join("\n")); - highlighted = highlighted.value.split("\n"); - - const highlighted_hunks = []; - let hunk_start = 0; - all_hunks.forEach((hunk, index) => { - const hunk_row_cnt = hunk.split("\n").length; - all_hunks[index] = props.patch.hunks[index].hunk.split("\n")[0] + all_hunks[index]; - highlighted_hunks.push(highlighted.slice(hunk_start, hunk_start + hunk_row_cnt)); - hunk_start = hunk_start + hunk_row_cnt; - }); - - all_hunks = all_hunks.map((hunk) => hunk.split("\n")); - - commit_patch = h("table", { cellspacing: "0px" }, [ - h("tbody", [ - props.patch.hunks.map((hunk, hunk_index) => { - let new_offset = 0; - let deleted_offset = 0; - const multiline_comments = []; - - return highlighted_hunks[hunk_index].map((line, line_index) => { - if(/^@@ -[0-9,]+ \+[0-9,]+ @@/.test(all_hunks[hunk_index][line_index])) { - new_offset++; + if(props.patch.too_large === true) { + return () => h("div", { class: "ps-3 pt-3 patch-too-large" }, [ + h("span", "Patch is too large to display.") + ]); + } + + // Array of hunks without the first chunk headers + const all_hunks = props.patch.hunks.map((hunk) => hunk.hunk.split("\n").slice(1).join("\n")); + + // Check if the patch's file extension matches any predefined language. + const language = hljs_languages.find((lang) => lang.extensions.some((extension) => props.patch.to.endsWith(extension))); + + // Syntax highlight all of the patch's hunks + const highlight_result = language + ? hljs.highlight(all_hunks.join("\n"), { language: language.name }) + : hljs.highlightAuto(all_hunks.join("\n")); + + const highlighted = highlight_result.value.split("\n"); + + const highlighted_hunks: string[][] = []; + + let hunk_start = 0; + all_hunks.forEach((hunk, index) => { + // Add the chunk headers back to the hunks + all_hunks[index] = props.patch.hunks[index].hunk.split("\n")[0] + all_hunks[index]; + + // Split the syntax highlighted patch back into hunks + const hunk_row_cnt = hunk.split("\n").length; + highlighted_hunks.push(highlighted.slice(hunk_start, hunk_start + hunk_row_cnt)); + + hunk_start = hunk_start + hunk_row_cnt; + }); + + const all_hunks_raw = all_hunks.map((hunk) => hunk.split("\n")); + + return () => h("table", { cellspacing: "0px" }, [ + h("tbody", [ + props.patch.hunks.map((hunk, hunk_index) => { + let new_offset = 0; + let deleted_offset = 0; + const multiline_comments: string[] = []; + + return highlighted_hunks[hunk_index].map((line, line_index) => { + if(/^@@ -[0-9,]+ \+[0-9,]+ @@/.test(all_hunks_raw[hunk_index][line_index])) { + return h("tr", { class: "commit-file-pos-change" }, [ + h("td", { "patch-line-col-unsel": "..." }), + h("td", { "patch-line-col-unsel": "..." }), + h("td", { "patch-line-col-unsel": "..." }), + h("td", [ + h("code", all_hunks_raw[hunk_index][line_index]) + ]) + ]); + } else if(/^\\ No newline at end of file$/.test(all_hunks_raw[hunk_index][line_index])) { + new_offset++; + deleted_offset++; + return h("tr", { class: "commit-file-no-newline" }, [ + h("td", ""), + h("td", ""), + h("td", ""), + h("td", [ + h("code", all_hunks_raw[hunk_index][line_index]) + ]) + ]); + } else { + let first_td: VNode; + let second_td: VNode; + let third_td: VNode; + + const adjusted_line_index = line_index + 1; + + if(hunk.new_lines.includes(adjusted_line_index)) { + first_td = h("td", ""); + second_td = h("td", { class: "line-highlight-new", "patch-line-col-unsel": Number(hunk.new_start) + line_index - new_offset }); + third_td = h("td", { class: "line-new", "patch-line-col-unsel": "+" }); deleted_offset++; - return h("tr", { class: "commit-file-pos-change" }, [ - h("td", { "patch-line-col-unsel": "..." }), - h("td", { "patch-line-col-unsel": "..." }), - h("td", { "patch-line-col-unsel": "..." }), - h("td", [ - h("code", all_hunks[hunk_index][line_index]) - ]) - ]); - } else if(/^\\ No newline at end of file$/.test(all_hunks[hunk_index][line_index])) { + } else if(hunk.deleted_lines.includes(adjusted_line_index)) { + first_td = h("td", { "patch-line-col-unsel": Number(hunk.old_start) + line_index - deleted_offset }); + second_td = h("td", { class: "line-highlight-deleted" }); + third_td = h("td", { class: "line-deleted", "patch-line-col-unsel": "-" }); new_offset++; - deleted_offset++; - return h("tr", { class: "commit-file-no-newline" }, [ - h("td", ""), - h("td", ""), - h("td", ""), - h("td", [ - h("code", all_hunks[hunk_index][line_index]) - ]) - ]); } else { - let first_td; - let second_td; - let third_td; - - if(hunk.new_lines.includes(line_index)) { - first_td = h("td", ""); - second_td = h("td", { class: "line-highlight-new", "patch-line-col-unsel": Number(hunk.new_start) + line_index - new_offset }); - third_td = h("td", { class: "line-new", "patch-line-col-unsel": "+" }); - deleted_offset++; - } else if(hunk.deleted_lines.includes(line_index)) { - first_td = h("td", { "patch-line-col-unsel": Number(hunk.old_start) + line_index - deleted_offset }); - second_td = h("td", { class: "line-highlight-deleted" }); - third_td = h("td", { class: "line-deleted", "patch-line-col-unsel": "-" }); - new_offset++; - } else { - first_td = h("td", { class: "line-unchanged", "patch-line-col-unsel": Number(hunk.old_start) + line_index - deleted_offset }); - second_td = h("td", { class: "line-unchanged", "patch-line-col-unsel": Number(hunk.new_start) + line_index - new_offset }); - third_td = h("td", ""); - } - - let comment_open = line.match(/<span class="hljs-comment">/g); - const comment_open_cnt = (comment_open !== null) ? comment_open.length : 0; - comment_open = (comment_open !== null) ? comment_open[0] : ""; - - let comment_close = line.match(/<\/span>/g); - const comment_close_cnt = (comment_close !== null) ? comment_close.length : 0; - comment_close = (comment_close !== null) ? comment_close[0] : ""; - - if(comment_open_cnt > comment_close_cnt) { - line = line + "</span>"; - console.log("Öppning " + line); - multiline_comments.push(comment_open); - } else if(comment_open_cnt < comment_close_cnt && multiline_comments.length !== 0) { - line = multiline_comments[multiline_comments.length - 1] + line; - console.log("Stängning " + line + " " + multiline_comments[multiline_comments.length - 1]); - multiline_comments.pop(); - } else if(multiline_comments.length !== 0) { - line = multiline_comments[multiline_comments.length - 1] + line + "</span>"; - console.log("Mitt i " + line); - } - - return h("tr", [ - first_td, - second_td, - third_td, - h("td", [ - h("code", { innerHTML: line }) - ]) - ]); + first_td = h("td", { class: "line-unchanged", "patch-line-col-unsel": Number(hunk.old_start) + line_index - deleted_offset }); + second_td = h("td", { class: "line-unchanged", "patch-line-col-unsel": Number(hunk.new_start) + line_index - new_offset }); + third_td = h("td", ""); + } + + const is_comment_open = line.match(/<span class="hljs-comment">/g); + const comment_open_cnt = (is_comment_open !== null) ? is_comment_open.length : 0; + const comment_open = (is_comment_open !== null) ? is_comment_open[0] : ""; + + const is_comment_close = line.match(/<\/span>/g); + const comment_close_cnt = (is_comment_close !== null) ? is_comment_close.length : 0; + // Const comment_close = (is_comment_close !== null) ? is_comment_close[0] : ""; + + if(comment_open_cnt > comment_close_cnt) { + line = line + "</span>"; + multiline_comments.push(comment_open); + } else if(comment_open_cnt < comment_close_cnt && multiline_comments.length !== 0) { + line = multiline_comments[multiline_comments.length - 1] + line; + multiline_comments.pop(); + } else if(multiline_comments.length !== 0) { + line = multiline_comments[multiline_comments.length - 1] + line + "</span>"; } - }); - }) - ]) - ]); - } else { - commit_patch = h("div", { class: "ps-3 pt-3 patch-too-large" }, [ - h("span", "Patch is too large to display.") - ]); - } - return () => commit_patch; + return h("tr", [ + first_td, + second_td, + third_td, + h("td", [ + h("code", { innerHTML: line }) + ]) + ]); + } + }); + }) + ]) + ]); } -}; +}); </script> diff --git a/packages/client/src/components/HomeHeader.vue b/packages/client/src/components/HomeHeader.vue index b108b9b..74e90f9 100644 --- a/packages/client/src/components/HomeHeader.vue +++ b/packages/client/src/components/HomeHeader.vue @@ -7,10 +7,10 @@ </div> </template> -<script> -import { ref } from "vue"; +<script lang="ts"> +import { defineComponent, ref } from "vue"; -export default { +export default defineComponent({ name: "HomeHeader", setup() { const title = ref(""); @@ -28,7 +28,7 @@ export default { mounted() { this.fetchInfo(); } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/HomeProjectsHeader.vue b/packages/client/src/components/HomeProjectsHeader.vue index f9aeb20..267ecb8 100644 --- a/packages/client/src/components/HomeProjectsHeader.vue +++ b/packages/client/src/components/HomeProjectsHeader.vue @@ -14,15 +14,17 @@ </div> </template> -<script> -import BaseButton from "@/components/BaseButton"; +<script lang="ts"> +import { defineComponent } from "vue"; -export default { +import BaseButton from "../components/BaseButton.vue"; + +export default defineComponent({ name: "HomeProjectsHeader", components: { BaseButton } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/RepositoryCloneDropdown.vue b/packages/client/src/components/RepositoryCloneDropdown.vue index a46d569..335604f 100644 --- a/packages/client/src/components/RepositoryCloneDropdown.vue +++ b/packages/client/src/components/RepositoryCloneDropdown.vue @@ -34,10 +34,11 @@ </div> </template> -<script> +<script lang="ts"> +import { defineComponent } from "vue"; import { createPopper } from "@popperjs/core"; -export default { +export default defineComponent({ name: "RepositoryCloneDropdown", props: { repository: { @@ -46,18 +47,25 @@ export default { } }, methods: { - async copyToClipboard(event) { - const url_box = document.getElementById("clone").getElementsByTagName("input")[0]; + async copyToClipboard(event: Event) { + const url_box = document.getElementById("clone")?.getElementsByTagName("input")[0]; + + if(!url_box) { + return; + } url_box.select(); url_box.setSelectionRange(0, 99999); document.execCommand("copy"); const copied_tooltip = document.querySelector("#copied-tooltip"); - copied_tooltip.classList.add("show"); - await new Promise(resolve => setTimeout(resolve, 2000)); - copied_tooltip.classList.remove("show"); + if(copied_tooltip) { + copied_tooltip.classList.add("show"); + + await new Promise(resolve => setTimeout(resolve, 2000)); + copied_tooltip.classList.remove("show"); + } }, getURL() { return `${window.location.protocol}//${window.location.host}/${this.repository}`; @@ -65,52 +73,64 @@ export default { }, mounted() { const dropdown_button = document.querySelector("#dropdown-button"); - const dropdown_menu = document.querySelector("#dropdown-menu"); + const dropdown_menu = document.getElementById("dropdown-menu"); - const copied_tooltip = document.querySelector("#copied-tooltip"); + const copied_tooltip = document.getElementById("copied-tooltip"); const copy = document.querySelector("#copy"); const tooltip_arrow = document.querySelector("#tooltip-arrow"); - createPopper(dropdown_button, dropdown_menu, { - placement: "bottom-end", - modifiers: [ - { - name: "offset", - options: { offset: [ 0, 2 ] } - } - ] - }); - createPopper(copy, copied_tooltip, { - placement: "top", - modifiers: [ - { - name: "offset", - options: { offset: [ 0, 10 ] } - }, - { - name: "arrow", - options: { element: tooltip_arrow } - } - ] - }); - const clickOutsideDropdown = (event) => { - if(!dropdown_menu.contains(event.target) && event.target !== dropdown_button) { + if(dropdown_button && dropdown_menu) { + createPopper(dropdown_button, dropdown_menu, { + placement: "bottom-end", + modifiers: [ + { + name: "offset", + options: { offset: [ 0, 2 ] } + } + ] + }); + } + + if(copy && copied_tooltip) { + createPopper(copy, copied_tooltip, { + placement: "top", + modifiers: [ + { + name: "offset", + options: { offset: [ 0, 10 ] } + }, + { + name: "arrow", + options: { element: tooltip_arrow } + } + ] + }); + } + + const clickOutsideDropdown = (event: Event) => { + const target = event.target as HTMLElement; + + if(dropdown_menu && dropdown_menu.contains(target) === false && target !== dropdown_button) { dropdown_menu.classList.remove("show"); document.removeEventListener("click", clickOutsideDropdown); } }; - dropdown_button.addEventListener("click", () => { - if(dropdown_menu.classList.contains("show")) { - dropdown_menu.classList.remove("show"); - document.removeEventListener("click", clickOutsideDropdown); - } else { - dropdown_menu.classList.add("show"); - document.addEventListener("click", clickOutsideDropdown); - } - }); + if(dropdown_button) { + dropdown_button.addEventListener("click", () => { + if(dropdown_menu) { + if(dropdown_menu.classList.contains("show")) { + dropdown_menu.classList.remove("show"); + document.removeEventListener("click", clickOutsideDropdown); + } else { + dropdown_menu.classList.add("show"); + document.addEventListener("click", clickOutsideDropdown); + } + } + }); + } } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/RepositoryHeader.vue b/packages/client/src/components/RepositoryHeader.vue index 8059809..4d75cfb 100644 --- a/packages/client/src/components/RepositoryHeader.vue +++ b/packages/client/src/components/RepositoryHeader.vue @@ -10,10 +10,12 @@ </div> </template> -<script> -import BaseBackButton from "@/components/BaseBackButton"; +<script lang="ts"> +import { defineComponent } from "vue"; -export default { +import BaseBackButton from "../components/BaseBackButton.vue"; + +export default defineComponent({ name: "RepositoryHeader", props: { name: { @@ -28,7 +30,7 @@ export default { components: { BaseBackButton } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/RepositoryNavbar.vue b/packages/client/src/components/RepositoryNavbar.vue index 08fa179..ca8e92d 100644 --- a/packages/client/src/components/RepositoryNavbar.vue +++ b/packages/client/src/components/RepositoryNavbar.vue @@ -22,10 +22,12 @@ </div> </template> -<script> -import RepositoryCloneDropdown from "@/components/RepositoryCloneDropdown"; +<script lang="ts"> +import { defineComponent } from "vue"; -export default { +import RepositoryCloneDropdown from "../components/RepositoryCloneDropdown.vue"; + +export default defineComponent({ name: "RepositoryNavbar", props: { repository: { @@ -54,7 +56,7 @@ export default { this.nav_items = [ "about" ].concat(this.nav_items); } } -}; +}); </script> <style lang="scss" scoped> diff --git a/packages/client/src/components/RepositoryTreeBlob.vue b/packages/client/src/components/RepositoryTreeBlob.vue index ac4ee32..60f0506 100644 --- a/packages/client/src/components/RepositoryTreeBlob.vue +++ b/packages/client/src/components/RepositoryTreeBlob.vue @@ -10,18 +10,17 @@ </tbody> </table> <span - v-else v-html="content_lines" + v-else v-html="content_blob" id="markdown-blob" /> </template> -<script> -import { ref } from "vue"; +<script lang="ts"> +import { defineComponent, Ref, ref } from "vue"; import hljs from "highlight.js"; import hljs_languages from "../util/hljs-languages"; -import path from "path"; -import marked from "@/lib/marked.min.js"; +import marked from "marked"; -export default { +export default defineComponent({ name: "RepositoryTreeBlob", props: { repository: { @@ -46,17 +45,29 @@ export default { this.initHighlightedContent(); }, setup(props) { - const content_lines = ref([]); - const is_markdown = ref(false); + const content_lines: Ref<string[]> = ref([]); + const content_blob: Ref<string> = ref(""); + const is_markdown: Ref<boolean> = ref(false); const initHighlightedContent = async() => { - if(path.extname(props.path) === ".md") { + const path_extension = /(?:\.([^.]+))?$/; + + const path_ext = path_extension.exec(props.path); + let ext = ""; + + if(path_ext) { + ext = `.${path_ext[1]}`; + } + + if(ext === ".md") { const markdown = document.createElement("html"); markdown.innerHTML = marked(props.content); const checkboxes = markdown.querySelectorAll("ul > li > input[type=\"checkbox\"]"); checkboxes.forEach((checkbox) => { - checkbox.parentElement.parentElement.classList.add("checkbox-list"); + if(checkbox.parentElement) { + checkbox.parentElement.classList.add("checkbox-list"); + } }); const codeblocks = markdown.querySelectorAll("code"); @@ -64,20 +75,20 @@ export default { codeblock.classList.add("markdown-codeblock"); }); - content_lines.value = markdown.innerHTML; + content_blob.value = markdown.innerHTML; is_markdown.value = true; return; } - const language = hljs_languages.find((lang) => lang.extensions.some((extension) => path.extname(props.path) === extension)); + const language = hljs_languages.find((lang) => lang.extensions.some((extension) => ext === extension)); const highlighted = language ? hljs.highlight(props.content, { language: language.name }) : hljs.highlightAuto(props.content); content_lines.value = highlighted.value.split("\n"); }; - return { content_lines, is_markdown, initHighlightedContent }; + return { content_lines, content_blob, is_markdown, initHighlightedContent }; } -}; +}); </script> <style lang="scss"> diff --git a/packages/client/src/components/RepositoryTreeTree.vue b/packages/client/src/components/RepositoryTreeTree.vue index 821251e..a17dc93 100644 --- a/packages/client/src/components/RepositoryTreeTree.vue +++ b/packages/client/src/components/RepositoryTreeTree.vue @@ -47,10 +47,11 @@ </table> </template> -<script> -const { formatDistance } = require("date-fns"); +<script lang="ts"> +import { defineComponent } from "vue"; +import { formatDistance } from "date-fns"; -export default { +export default defineComponent({ name: "RepositoryTreeTree", props: { repository: { @@ -67,19 +68,19 @@ export default { } }, methods: { - stopClick(event) { + stopClick(event: Event) { event.preventDefault(); }, - routeToCommit(commit_id, event) { + routeToCommit(commit_id: string, event: Event) { event.stopPropagation(); event.preventDefault(); this.$router.push(`/${this.repository}/log/${commit_id}`); }, - getPrettyLastUpdated(date) { + getPrettyLastUpdated(date: number) { return formatDistance(new Date(date * 1000), new Date(), { addSuffix: true }); } } -}; +}); </script> <style lang="scss" scoped> |