aboutsummaryrefslogtreecommitdiff
path: root/packages/client/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'packages/client/src/components')
-rw-r--r--packages/client/src/components/BaseBackButton.vue2
-rw-r--r--packages/client/src/components/BaseBreadcrumb.vue25
-rw-r--r--packages/client/src/components/BaseButton.vue29
-rw-r--r--packages/client/src/components/CommitPatch.vue6
-rw-r--r--packages/client/src/components/HomeHeader.vue24
-rw-r--r--packages/client/src/components/HomeProjectsHeader.vue27
-rw-r--r--packages/client/src/components/RepositoryCloneDropdown.vue215
-rw-r--r--packages/client/src/components/RepositoryHeader.vue21
-rw-r--r--packages/client/src/components/RepositoryNavbar.vue97
-rw-r--r--packages/client/src/components/RepositoryTreeBlob.vue9
-rw-r--r--packages/client/src/components/RepositoryTreeTree.vue62
11 files changed, 386 insertions, 131 deletions
diff --git a/packages/client/src/components/BaseBackButton.vue b/packages/client/src/components/BaseBackButton.vue
index e1191f9..2de0c77 100644
--- a/packages/client/src/components/BaseBackButton.vue
+++ b/packages/client/src/components/BaseBackButton.vue
@@ -1,5 +1,5 @@
<template>
- <div class="d-inline">
+ <div>
<router-link :to="to">
<svg
xmlns="http://www.w3.org/2000/svg" id="back"
diff --git a/packages/client/src/components/BaseBreadcrumb.vue b/packages/client/src/components/BaseBreadcrumb.vue
index 91c0109..65dffd4 100644
--- a/packages/client/src/components/BaseBreadcrumb.vue
+++ b/packages/client/src/components/BaseBreadcrumb.vue
@@ -33,7 +33,28 @@ export default {
<style lang="scss" scoped>
@use "../scss/colors";
-@import "../scss/bootstrap";
-@import "~bootstrap/scss/breadcrumb";
+.breadcrumb {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0 0;
+ margin-bottom: 1rem;
+ list-style: none;
+ a {
+ color: colors.$text;
+ text-decoration: none;
+ }
+ .active {
+ color: #6c757d;
+ }
+ .breadcrumb-item {
+ padding-left: 0.5rem;
+ }
+ .breadcrumb-item + .active::before {
+ float: left;
+ padding-right: 0.5rem;
+ color: #6c757d;
+ content: "/";
+ }
+}
</style>
diff --git a/packages/client/src/components/BaseButton.vue b/packages/client/src/components/BaseButton.vue
new file mode 100644
index 0000000..2a528b0
--- /dev/null
+++ b/packages/client/src/components/BaseButton.vue
@@ -0,0 +1,29 @@
+<template>
+ <button :type="type ? type : ''">
+ {{ value }}
+ </button>
+</template>
+
+<script>
+export default {
+ name: "BaseButton",
+ props: {
+ type: {
+ type: String,
+ required: true
+ },
+ value: {
+ type: String,
+ required: true
+ }
+ }
+};
+</script>
+
+<style lang="scss" scoped>
+@use "../scss/mixins";
+
+button {
+ @include mixins.button;
+}
+</style>
diff --git a/packages/client/src/components/CommitPatch.vue b/packages/client/src/components/CommitPatch.vue
index cf15eda..fdeb28d 100644
--- a/packages/client/src/components/CommitPatch.vue
+++ b/packages/client/src/components/CommitPatch.vue
@@ -15,17 +15,17 @@ export default {
let commit_patch;
if(props.patch.too_large === false) {
- let all_hunks = props.patch.hunks.map((hunk) => hunk.hunk);
+ 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"));
- console.log(highlighted);
highlighted = highlighted.value.split("\n");
const highlighted_hunks = [];
let hunk_start = 0;
- all_hunks.forEach((hunk) => {
+ 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;
});
diff --git a/packages/client/src/components/HomeHeader.vue b/packages/client/src/components/HomeHeader.vue
index 24afd5b..b108b9b 100644
--- a/packages/client/src/components/HomeHeader.vue
+++ b/packages/client/src/components/HomeHeader.vue
@@ -1,11 +1,9 @@
<template>
- <div id="header" class="d-flex mt-3 mb-3 ms-2">
- <div class="d-inline ms-3">
- <span id="title" class="fs-1">{{ title }}</span>
- <p id="about" class="mb-3 fs-4">
- {{ about }}
- </p>
- </div>
+ <div id="header">
+ <span id="title" class="fs-1">{{ title }}</span>
+ <p id="about" class="fs-4">
+ {{ about }}
+ </p>
</div>
</template>
@@ -33,11 +31,17 @@ export default {
};
</script>
-<style lang="scss">
-@import "../scss/fonts";
+<style lang="scss" scoped>
+@use "../scss/mixins";
+@use "../scss/fonts";
+
+#header {
+ @include mixins.header;
+ flex-direction: column;
+}
#title {
- font-family: $font-title;
+ font-family: fonts.$title;
font-weight: 300;
line-height: 0.6;
}
diff --git a/packages/client/src/components/HomeProjectsHeader.vue b/packages/client/src/components/HomeProjectsHeader.vue
index 5966e99..f9aeb20 100644
--- a/packages/client/src/components/HomeProjectsHeader.vue
+++ b/packages/client/src/components/HomeProjectsHeader.vue
@@ -1,34 +1,39 @@
<template>
- <div class="d-flex align-items-center flex-wrap">
- <div id="projects-header" class="ms-4">
+ <div id="projects">
+ <div id="projects-header">
<span class="fs-1">
Projects
</span>
</div>
- <div id="projects-search" class="d-flex">
+ <div id="projects-search">
<form>
<input type="search" name="q">
- <input
- type="submit" value="Search"
- class="btn btn-primary">
+ <BaseButton type="submit" value="Search" />
</form>
</div>
</div>
</template>
<script>
+import BaseButton from "@/components/BaseButton";
+
export default {
- name: "HomeProjectsHeader"
+ name: "HomeProjectsHeader",
+ components: {
+ BaseButton
+ }
};
</script>
<style lang="scss" scoped>
-@use "../scss/colors";
-@import "../scss/bootstrap";
-
-@import "~bootstrap/scss/buttons";
+#projects {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+}
#projects-search {
+ display: flex;
align-items: center;
margin-left: auto;
margin-right: 15px;
diff --git a/packages/client/src/components/RepositoryCloneDropdown.vue b/packages/client/src/components/RepositoryCloneDropdown.vue
index ed565ef..a46d569 100644
--- a/packages/client/src/components/RepositoryCloneDropdown.vue
+++ b/packages/client/src/components/RepositoryCloneDropdown.vue
@@ -1,36 +1,41 @@
<template>
- <div id="clone" class="d-flex align-items-center">
- <div class="dropdown">
- <button
- class="btn btn-primary btn-sm dropdown-toggle" type="button"
- id="dropdownMenuButton1" data-bs-toggle="dropdown"
- data-bs-auto-close="outside" aria-expanded="false">
- Clone
- </button>
- <ul class="dropdown-menu dropdown-menu-end dropdown-menu-dark" aria-labelledby="dropdownMenuButton1">
- <li class="pt-2">
- <span class="ms-2 fs-5 fw-bold">Clone with HTTP</span>
- <label id="clone-url-copy">
- <input
- type="text" :value="getURL()"
- class="form-control form-control-sm ms-2 me-2" readonly>
- <svg
- xmlns="http://www.w3.org/2000/svg" height="18px"
- viewBox="0 0 24 24" width="18px"
- fill="#FFFFFF" @click="copyToClipboard">
- <path d="M0 0h24v24H0z" fill="none" />
- <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" />
- </svg>
- </label>
- </li>
- </ul>
+ <div id="clone">
+ <button
+ id="dropdown-button" aria-describedby="dropdown-menu"
+ type="button">
+ Clone
+ </button>
+ <div id="dropdown-menu" role="dropdown-menu">
+ <div>
+ <p id="dropdown-title" class="fs-5">
+ Clone with HTTP
+ </p>
+ <div
+ id="copied-tooltip" role="copied-tooltip"
+ class="fs-5">
+ <div id="tooltip-arrow" />
+ Copied!
+ </div>
+ <label id="clone-url-copy">
+ <input
+ type="text" :value="getURL()"
+ readonly>
+ <svg
+ xmlns="http://www.w3.org/2000/svg" height="18px"
+ viewBox="0 0 24 24" width="18px"
+ fill="#FFFFFF" @click="copyToClipboard"
+ id="copy">
+ <path d="M0 0h24v24H0z" fill="none" />
+ <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" />
+ </svg>
+ </label>
+ </div>
</div>
</div>
</template>
<script>
-import { Tooltip } from "bootstrap/dist/js/bootstrap.esm";
-// Import Fan from "../util/sleep.worker";
+import { createPopper } from "@popperjs/core";
export default {
name: "RepositoryCloneDropdown",
@@ -48,41 +53,74 @@ export default {
url_box.setSelectionRange(0, 99999);
document.execCommand("copy");
- event.stopPropagation();
+ const copied_tooltip = document.querySelector("#copied-tooltip");
+ copied_tooltip.classList.add("show");
- const exampleEl = document.getElementById("clone-url-copy").getElementsByTagName("svg")[0];
- const tooltip = new Tooltip(exampleEl, { title: "Copied the URL", trigger: "manual" });
- console.log(tooltip);
- tooltip.show();
-
- await new Promise(resolve => setTimeout(resolve, 1700));
- tooltip.hide();
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ copied_tooltip.classList.remove("show");
},
getURL() {
return `${window.location.protocol}//${window.location.host}/${this.repository}`;
}
+ },
+ mounted() {
+ const dropdown_button = document.querySelector("#dropdown-button");
+ const dropdown_menu = document.querySelector("#dropdown-menu");
+
+ const copied_tooltip = document.querySelector("#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) {
+ 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);
+ }
+ });
}
};
</script>
<style lang="scss" scoped>
@use "../scss/colors";
-@import "../scss/bootstrap";
-
-$dropdown-dark-bg: lighten(#000000, 10%);
-
-@import "~bootstrap/scss/buttons";
-@import "~bootstrap/scss/dropdown";
-@import "~bootstrap/scss/forms";
-@import "~bootstrap/scss/tooltip";
-
-.form-control {
- width: auto;
-}
+@use "../scss/mixins";
+@use "../scss/fonts";
#clone {
- margin-left: auto;
- margin-right: 40px;
+ display: flex;
+ align-items: center;
}
#clone-url-copy {
@@ -95,6 +133,14 @@ $dropdown-dark-bg: lighten(#000000, 10%);
display: inline-block;
padding-right: 30px;
min-height: 0;
+ background: lighten(#000000, 15%);
+ border: 1px solid lighten(#000000, 28%);
+ color: colors.$text;
+ border-radius: 2px;
+ height: 25px;
+ margin-left: 0.5rem;
+ margin-right: 0.5rem;
+ font-family: fonts.$primary;
}
svg {
content: "";
@@ -109,4 +155,75 @@ $dropdown-dark-bg: lighten(#000000, 10%);
}
}
}
+
+#dropdown-button {
+ @include mixins.button;
+ padding: 3px 5px 3px 6px;
+ font-size: 15px;
+ font-family: fonts.$primary;
+ &::after {
+ display: inline-block;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0;
+ border-left: 0.3em solid transparent;
+ }
+}
+
+#dropdown-menu {
+ visibility: hidden;
+ pointer-events: none;
+ background-color: lighten(#000000, 12%);
+ opacity: 0;
+ transition:visibility 0.08s linear,opacity 0.08s linear;
+ border-radius: 5px;
+ z-index: 1000;
+ position: absolute;
+}
+
+#copied-tooltip {
+ visibility: hidden;
+ pointer-events: none;
+ position: absolute;
+ background-color: #000000;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ border-radius: 3px;
+ z-index: 99999;
+ opacity: 0;
+ transition:visibility 0.4s linear,opacity 0.4s linear;
+}
+
+.show {
+ visibility: visible !important;
+ pointer-events: all !important;
+ opacity: 1 !important;
+}
+
+#tooltip-arrow {
+ bottom: -4px;
+}
+
+#tooltip-arrow,
+#tooltip-arrow::before {
+ position: absolute;
+ width: 8px;
+ height: 8px;
+ z-index: -1;
+}
+
+#tooltip-arrow::before {
+ content: '';
+ transform: rotate(45deg);
+ background: #000000;
+}
+
+#dropdown-title {
+ margin-left: 0.5rem;
+ font-weight: 700;
+}
</style>
diff --git a/packages/client/src/components/RepositoryHeader.vue b/packages/client/src/components/RepositoryHeader.vue
index 50a1db9..8059809 100644
--- a/packages/client/src/components/RepositoryHeader.vue
+++ b/packages/client/src/components/RepositoryHeader.vue
@@ -1,7 +1,7 @@
<template>
- <div id="header" class="mx-0 d-flex mt-3 ms-2">
+ <div id="header">
<BaseBackButton to="/" />
- <div class="d-inline ms-3">
+ <div id="header-content">
<span id="title" class="fs-1">{{ name }}</span>
<p id="about" class="fs-4">
{{ description }}
@@ -33,12 +33,23 @@ export default {
<style lang="scss" scoped>
@use "../scss/colors";
+@use "../scss/mixins";
+@use "../scss/fonts";
-@import "../scss/bootstrap";
-@import "../scss/fonts";
+#header {
+ @include mixins.header;
+ flex-direction: row;
+ div {
+ display: inline;
+ }
+}
+
+#header-content {
+ margin-left: 1rem;
+}
#title {
- font-family: $font-title;
+ font-family: fonts.$title;
font-weight: 300;
line-height: 0.6;
}
diff --git a/packages/client/src/components/RepositoryNavbar.vue b/packages/client/src/components/RepositoryNavbar.vue
index f709976..08fa179 100644
--- a/packages/client/src/components/RepositoryNavbar.vue
+++ b/packages/client/src/components/RepositoryNavbar.vue
@@ -1,27 +1,24 @@
<template>
<div id="navbar">
- <div id="repo-navbar" class="ms-4 ps-4">
- <nav class="navbar navbar-expand navbar-dark">
- <div class="container-fluid px-0">
- <div class="collapse navbar-collapse">
- <ul class="navbar-nav align-items-center flex-fill">
- <li
- v-for="(item, index) in nav_items" :key="index"
- class="nav-item">
- <router-link
- class="nav-link fs-4" :class="{ active: activePage === item }"
- :aria-current="(activePage === item) ? 'page' : ''" :to="'/' + repository + '/' + item">
- {{ item }}
- </router-link>
- </li>
- <li class="nav-item ms-auto me-4">
- <RepositoryCloneDropdown :repository="repository" class="d-block" />
- </li>
- </ul>
- </div>
+ <nav class="navbar">
+ <div class="navbar-container">
+ <div class="nav">
+ <ul>
+ <li
+ v-for="(item, index) in nav_items" :key="index">
+ <router-link
+ class="nav-link fs-4" :class="{ active: activePage === item }"
+ :aria-current="(activePage === item) ? 'page' : ''" :to="'/' + repository + '/' + item">
+ {{ item }}
+ </router-link>
+ </li>
+ <li id="clone-dropdown">
+ <RepositoryCloneDropdown :repository="repository" class="d-block" />
+ </li>
+ </ul>
</div>
- </nav>
- </div>
+ </div>
+ </nav>
</div>
</template>
@@ -54,7 +51,6 @@ export default {
},
watch: {
hasReadme() {
- console.log("HEEEJ");
this.nav_items = [ "about" ].concat(this.nav_items);
}
}
@@ -63,12 +59,59 @@ export default {
<style lang="scss" scoped>
@use "../scss/colors";
-@import "../scss/bootstrap";
-
-@import "~bootstrap/scss/nav";
-@import "~bootstrap/scss/navbar";
#navbar {
- line-height: 0;
+ margin-left: 3rem;
+}
+
+.navbar {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ flex-wrap: nowrap;
+ justify-content: flex-start;
+ .navbar-container {
+ display: flex;
+ flex-wrap: inherit;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ .nav {
+ display: flex;
+ flex-basis: auto;
+ flex-grow: 1;
+ align-items: center;
+ ul {
+ display: flex;
+ flex-direction: row;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+ align-items: center;
+ flex: 1 1 auto;
+ .nav-link {
+ color: darken(#ffffff, 50%);
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ transition: color .15s ease-in-out;
+ &:hover {
+ color: darken(#ffffff, 20%);
+ }
+ }
+ #clone-dropdown {
+ margin-left: auto;
+ margin-right: 40px;
+ }
+ .active {
+ color: darken(#ffffff, 20%);
+ }
+ }
+ }
+ }
}
+
</style>
diff --git a/packages/client/src/components/RepositoryTreeBlob.vue b/packages/client/src/components/RepositoryTreeBlob.vue
index 2a5a6dc..ac4ee32 100644
--- a/packages/client/src/components/RepositoryTreeBlob.vue
+++ b/packages/client/src/components/RepositoryTreeBlob.vue
@@ -10,7 +10,8 @@
</tbody>
</table>
<span
- v-else v-html="content_lines" />
+ v-else v-html="content_lines"
+ id="markdown-blob" />
</template>
<script>
@@ -81,9 +82,9 @@ export default {
<style lang="scss">
@use "../scss/colors";
+@use "../scss/fonts";
@import "~highlight.js/scss/srcery.scss";
-@import "../scss/fonts";
ul {
padding-left: 30px;
@@ -99,10 +100,10 @@ code {
white-space: pre-wrap;
word-wrap: anywhere;
background-color: lighten(#000000, 8%);
- font-family: $font-primary;
+ font-family: fonts.$primary;
}
-span {
+#markdown-blob {
word-wrap: anywhere;
a {
color: colors.$primary-light;
diff --git a/packages/client/src/components/RepositoryTreeTree.vue b/packages/client/src/components/RepositoryTreeTree.vue
index 376cafc..21f9570 100644
--- a/packages/client/src/components/RepositoryTreeTree.vue
+++ b/packages/client/src/components/RepositoryTreeTree.vue
@@ -10,7 +10,7 @@
<tbody>
<tr v-if="path !== ''" @click="$router.push(`/${repository}/tree/${path.split('/').slice(0, -1).join('/') }`)">
<td
- class="d-flex align-items-center">
+ class="flex-center">
<div class="tree-entry-padding" />
..
</td>
@@ -20,7 +20,7 @@
<tr
v-for="(entry, entry_name, index) in tree" :key="index"
@click="$router.push(`/${repository}/tree${path ? '/' + path : ''}/${entry_name}`)">
- <td class="d-flex align-items-center">
+ <td class="flex-center">
<svg
xmlns="http://www.w3.org/2000/svg" height="18px"
viewBox="0 0 24 24" width="18px"
@@ -82,33 +82,57 @@ export default {
<style lang="scss" scoped>
@use "../scss/colors";
-@import "../scss/bootstrap";
-#tree {
+table {
border-spacing: 0;
+ width: 100%;
+ margin-bottom: 1rem;
+ tbody {
+ vertical-align: inherit;
+ tr {
+ &:hover {
+ background-color: lighten(colors.$background, 5%);
+ }
+ td {
+ padding-bottom: 1em;
+ &:nth-child(2) a, &:nth-child(3) {
+ font-weight: 300;
+ }
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 2vw;
+ }
+ }
+ }
+ thead {
+ vertical-align: bottom;
+ }
th {
- padding-bottom: 5px;
- color: colors.$secondary;
text-align: start;
- padding-right: 20px;
- }
- tbody tr:hover {
- background-color: lighten(colors.$background, 10%);
+ padding-bottom: 1em;
+ color: colors.$secondary;
}
- td {
- padding-top: 5px;
- padding-bottom: 5px;
- padding-right: 2vw;
- &:nth-child(2) a, &:nth-child(3) {
- font-weight: 300;
- }
+ > :not(caption) > * > * {
+ padding: 0.2rem 1rem;
+ border-bottom-width: 1px;
}
.tree-entry-padding, svg {
width: 18px;
padding-right: 5px;
}
- a {
- padding-right: 18px;
+}
+
+#log {
+ padding-left: 0;
+}
+
+@media (max-width: 576px) {
+ table > :not(caption) > * > * {
+ padding: 0.1rem;
}
}
+.flex-center {
+ display: flex;
+ align-items: center;
+}
</style>