Add filetree on left of diff view (#21012)
This PR adds a filetree to the left side of the files/diff view. Initially the filetree will not be shown and may be shown via a new "Show file tree" button. Showing and hiding is using the same icon as github. Folders are collapsible. On small devices (max-width 991 PX) the file tree will be hidden. Close #18192 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
525751243e
commit
31f934c1d8
|
@ -1,7 +1,7 @@
|
||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository diff">
|
<div class="page-content repository diff">
|
||||||
{{template "repo/header" .}}
|
{{template "repo/header" .}}
|
||||||
<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
|
<div class="ui container fluid padded">
|
||||||
{{$class := ""}}
|
{{$class := ""}}
|
||||||
{{if .Commit.Signature}}
|
{{if .Commit.Signature}}
|
||||||
{{$class = (printf "%s%s" $class " isSigned")}}
|
{{$class = (printf "%s%s" $class " isSigned")}}
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<div>
|
<div>
|
||||||
<div class="diff-detail-box diff-box sticky df sb ac fw">
|
<div class="diff-detail-box diff-box sticky df sb ac fw">
|
||||||
|
<a class="diff-toggle-file-tree-button">
|
||||||
|
{{/* the icon meaning is reversed here, "octicon-sidebar-collapse" means show the file tree */}}
|
||||||
|
{{svg "octicon-sidebar-collapse" 16 "icon hide"}}
|
||||||
|
{{svg "octicon-sidebar-expand" 16 "icon"}}
|
||||||
|
</a>
|
||||||
<div class="diff-detail-stats df ac">
|
<div class="diff-detail-stats df ac">
|
||||||
{{svg "octicon-diff" 16 "mr-2"}}{{.locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}}
|
{{svg "octicon-diff" 16 "mr-2"}}{{.locale.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,33 +36,38 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ol class="diff-detail-box diff-stats m-0 hide" id="diff-files">
|
<script id="diff-data-script">
|
||||||
{{range .Diff.Files}}
|
(() => {
|
||||||
<li>
|
const diffData = {
|
||||||
<div class="bold df ac pull-right">
|
files: [{{range $i, $file := .Diff.Files}}{Name:"{{$file.Name}}",NameHash:"{{$file.NameHash}}",Type:{{$file.Type}},IsBin:{{$file.IsBin}},Addition:{{$file.Addition}},Deletion:{{$file.Deletion}}},{{end}}],
|
||||||
{{if .IsBin}}
|
isIncomplete: {{.Diff.IsIncomplete}},
|
||||||
<span class="ml-1 mr-3">
|
tooManyFilesMessage: "{{$.locale.Tr "repo.diff.too_many_files"}}",
|
||||||
{{$.locale.Tr "repo.diff.bin"}}
|
binaryFileMessage: "{{$.locale.Tr "repo.diff.bin"}}",
|
||||||
</span>
|
showMoreMessage: "{{.locale.Tr "repo.diff.show_more"}}",
|
||||||
{{else}}
|
statisticsMessage: "{{.locale.Tr "repo.diff.stats_desc_file"}}",
|
||||||
{{template "repo/diff/stats" dict "file" . "root" $}}
|
fileTreeIsVisible: false,
|
||||||
{{end}}
|
fileListIsVisible: false,
|
||||||
</div>
|
isLoadingNewData: false,
|
||||||
<!-- todo finish all file status, now modify, add, delete and rename -->
|
diffEnd: {{.Diff.End}},
|
||||||
<span class="status {{DiffTypeToStr .GetType}} tooltip" data-content="{{DiffTypeToStr .GetType}}" data-position="right center"> </span>
|
link: "{{$.Link}}"
|
||||||
<a class="file mono" href="#diff-{{.NameHash}}">{{.Name}}</a>
|
};
|
||||||
</li>
|
if(window.config.pageData.diffFileInfo) {
|
||||||
{{end}}
|
// Page is already loaded - add the data to our existing data
|
||||||
{{if .Diff.IsIncomplete}}
|
window.config.pageData.diffFileInfo.files.push(...diffData.files);
|
||||||
<li id="diff-too-many-files-stats" class="pt-2">
|
window.config.pageData.diffFileInfo.isIncomplete = diffData.isIncomplete;
|
||||||
<span class="file df ac sb">{{$.locale.Tr "repo.diff.too_many_files"}}
|
window.config.pageData.diffFileInfo.diffEnd = diffData.diffEnd;
|
||||||
<a class="ui basic tiny button" id="diff-show-more-files-stats" data-href="{{$.Link}}?skip-to={{.Diff.End}}&file-only=true">{{.locale.Tr "repo.diff.show_more"}}</a>
|
window.config.pageData.diffFileInfo.link = diffData.link;
|
||||||
</span>
|
} else {
|
||||||
</li>
|
// new load of page - populate initial data
|
||||||
{{end}}
|
window.config.pageData.diffFileInfo = diffData;
|
||||||
</ol>
|
}
|
||||||
<div id="diff-file-boxes">
|
})();
|
||||||
{{range $file := .Diff.Files}}
|
</script>
|
||||||
|
<div id="diff-file-list-container"></div>
|
||||||
|
<div id="diff-container">
|
||||||
|
<div id="diff-file-tree-container"></div>
|
||||||
|
<div id="diff-file-boxes" class="sixteen wide column">
|
||||||
|
{{range $i, $file := .Diff.Files}}
|
||||||
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}
|
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}
|
||||||
{{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}}
|
{{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}}
|
||||||
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
|
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
|
||||||
|
@ -171,6 +181,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{if not $.Repository.IsArchived}}
|
{{if not $.Repository.IsArchived}}
|
||||||
<div class="hide" id="edit-content-form">
|
<div class="hide" id="edit-content-form">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "base/head" .}}
|
{{template "base/head" .}}
|
||||||
<div class="page-content repository diff {{if .PageIsComparePull}}compare pull{{end}}">
|
<div class="page-content repository diff {{if .PageIsComparePull}}compare pull{{end}}">
|
||||||
{{template "repo/header" .}}
|
{{template "repo/header" .}}
|
||||||
<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
|
<div class="ui container fluid padded">
|
||||||
|
|
||||||
<h2 class="ui header">
|
<h2 class="ui header">
|
||||||
{{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}}
|
{{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="ui dropdown tiny basic button icon-button tooltip" data-content="{{.locale.Tr "repo.diff.options_button"}}">
|
<div class="ui dropdown tiny basic button icon-button tooltip" data-content="{{.locale.Tr "repo.diff.options_button"}}">
|
||||||
{{svg "octicon-kebab-horizontal"}}
|
{{svg "octicon-kebab-horizontal"}}
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<a class="item tiny basic toggle button" data-target="#diff-files">{{.locale.Tr "repo.diff.show_diff_stats"}}</a>
|
<a class="item tiny basic toggle button" id="show-file-list-btn">{{.locale.Tr "repo.diff.show_diff_stats"}}</a>
|
||||||
{{if .Issue.Index}}
|
{{if .Issue.Index}}
|
||||||
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{.locale.Tr "repo.diff.download_patch"}}</a>
|
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{.locale.Tr "repo.diff.download_patch"}}</a>
|
||||||
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{.locale.Tr "repo.diff.download_diff"}}</a>
|
<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{.locale.Tr "repo.diff.download_diff"}}</a>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<div class="page-content repository view issue pull files diff">
|
<div class="page-content repository view issue pull files diff">
|
||||||
{{template "repo/header" .}}
|
{{template "repo/header" .}}
|
||||||
<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
|
<div class="ui container fluid padded">
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
{{template "repo/issue/navbar" .}}
|
{{template "repo/issue/navbar" .}}
|
||||||
<div class="ui right">
|
<div class="ui right">
|
||||||
|
|
81
web_src/js/components/DiffFileList.vue
Normal file
81
web_src/js/components/DiffFileList.vue
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<template>
|
||||||
|
<ol class="diff-detail-box diff-stats m-0" id="diff-files" v-if="fileListIsVisible">
|
||||||
|
<li v-for="file in files" :key="file.NameHash">
|
||||||
|
<div class="bold df ac pull-right">
|
||||||
|
<span v-if="file.IsBin" class="ml-1 mr-3">{{ binaryFileMessage }}</span>
|
||||||
|
{{ file.IsBin ? '' : file.Addition + file.Deletion }}
|
||||||
|
<span v-if="!file.IsBin" class="diff-stats-bar tooltip mx-3" :data-content="statisticsMessage.replace('%d', (file.Addition + file.Deletion)).replace('%d', file.Addition).replace('%d', file.Deletion)">
|
||||||
|
<div class="diff-stats-add-bar" :style="{ 'width': diffStatsWidth(file.Addition, file.Deletion) }" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- todo finish all file status, now modify, add, delete and rename -->
|
||||||
|
<span :class="['status', diffTypeToString(file.Type), 'tooltip']" :data-content="diffTypeToString(file.Type)" data-position="right center"> </span>
|
||||||
|
<a class="file mono" :href="'#diff-' + file.NameHash">{{ file.Name }}</a>
|
||||||
|
</li>
|
||||||
|
<li v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2">
|
||||||
|
<span class="file df ac sb">{{ tooManyFilesMessage }}
|
||||||
|
<a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {initTooltip} from '../modules/tippy.js';
|
||||||
|
import {doLoadMoreFiles} from '../features/repo-diff.js';
|
||||||
|
|
||||||
|
const {pageData} = window.config;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DiffFileList',
|
||||||
|
|
||||||
|
data: () => {
|
||||||
|
return pageData.diffFileInfo;
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
fileListIsVisible(newValue) {
|
||||||
|
if (newValue === true) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
for (const el of this.$el.querySelectorAll('.tooltip')) {
|
||||||
|
initTooltip(el);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
document.getElementById('show-file-list-btn').addEventListener('click', this.toggleFileList);
|
||||||
|
},
|
||||||
|
|
||||||
|
unmounted() {
|
||||||
|
document.getElementById('show-file-list-btn').removeEventListener('click', this.toggleFileList);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleFileList() {
|
||||||
|
this.fileListIsVisible = !this.fileListIsVisible;
|
||||||
|
},
|
||||||
|
diffTypeToString(pType) {
|
||||||
|
const diffTypes = {
|
||||||
|
1: 'add',
|
||||||
|
2: 'modify',
|
||||||
|
3: 'del',
|
||||||
|
4: 'rename',
|
||||||
|
5: 'copy',
|
||||||
|
};
|
||||||
|
return diffTypes[pType];
|
||||||
|
},
|
||||||
|
diffStatsWidth(adds, dels) {
|
||||||
|
return `${adds / (adds + dels) * 100}%`;
|
||||||
|
},
|
||||||
|
loadMoreData() {
|
||||||
|
this.isLoadingNewData = true;
|
||||||
|
doLoadMoreFiles(this.link, this.diffEnd, () => {
|
||||||
|
this.isLoadingNewData = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
129
web_src/js/components/DiffFileTree.vue
Normal file
129
web_src/js/components/DiffFileTree.vue
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-show="fileTreeIsVisible"
|
||||||
|
id="diff-file-tree"
|
||||||
|
class="mr-3 mt-3 diff-detail-box"
|
||||||
|
>
|
||||||
|
<!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
|
||||||
|
<div class="ui list" v-if="fileTreeIsVisible">
|
||||||
|
<DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item" />
|
||||||
|
</div>
|
||||||
|
<div v-if="isIncomplete" id="diff-too-many-files-stats" class="pt-2">
|
||||||
|
<span>{{ tooManyFilesMessage }}</span><a :class="['ui', 'basic', 'tiny', 'button', isLoadingNewData === true ? 'disabled' : '']" id="diff-show-more-files-stats" @click.stop="loadMoreData">{{ showMoreMessage }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DiffFileTreeItem from './DiffFileTreeItem.vue';
|
||||||
|
import {doLoadMoreFiles} from '../features/repo-diff.js';
|
||||||
|
|
||||||
|
const {pageData} = window.config;
|
||||||
|
const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DiffFileTree',
|
||||||
|
components: {DiffFileTreeItem},
|
||||||
|
|
||||||
|
data: () => {
|
||||||
|
const fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) === 'true';
|
||||||
|
pageData.diffFileInfo.fileTreeIsVisible = fileTreeIsVisible;
|
||||||
|
return pageData.diffFileInfo;
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
fileTree() {
|
||||||
|
const result = [];
|
||||||
|
for (const file of this.files) {
|
||||||
|
// Split file into directories
|
||||||
|
const splits = file.Name.split('/');
|
||||||
|
let index = 0;
|
||||||
|
let parent = null;
|
||||||
|
let isFile = false;
|
||||||
|
for (const split of splits) {
|
||||||
|
index += 1;
|
||||||
|
// reached the end
|
||||||
|
if (index === splits.length) {
|
||||||
|
isFile = true;
|
||||||
|
}
|
||||||
|
let newParent = {
|
||||||
|
name: split,
|
||||||
|
children: [],
|
||||||
|
isFile
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isFile === true) {
|
||||||
|
newParent.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
// check if the folder already exists
|
||||||
|
const existingFolder = parent.children.find(
|
||||||
|
(x) => x.name === split
|
||||||
|
);
|
||||||
|
if (existingFolder) {
|
||||||
|
newParent = existingFolder;
|
||||||
|
} else {
|
||||||
|
parent.children.push(newParent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const existingFolder = result.find((x) => x.name === split);
|
||||||
|
if (existingFolder) {
|
||||||
|
newParent = existingFolder;
|
||||||
|
} else {
|
||||||
|
result.push(newParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent = newParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const mergeChildIfOnlyOneDir = (entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.children) {
|
||||||
|
mergeChildIfOnlyOneDir(entry.children);
|
||||||
|
}
|
||||||
|
if (entry.children.length === 1 && entry.children[0].isFile === false) {
|
||||||
|
// Merge it to the parent
|
||||||
|
entry.name = `${entry.name}/${entry.children[0].name}`;
|
||||||
|
entry.children = entry.children[0].children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Merge folders with just a folder as children in order to
|
||||||
|
// reduce the depth of our tree.
|
||||||
|
mergeChildIfOnlyOneDir(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// ensure correct buttons when we are mounted to the dom
|
||||||
|
this.adjustToggleButton(this.fileTreeIsVisible);
|
||||||
|
document.querySelector('.diff-toggle-file-tree-button').addEventListener('click', this.toggleVisibility);
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
document.querySelector('.diff-toggle-file-tree-button').removeEventListener('click', this.toggleVisibility);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleVisibility() {
|
||||||
|
this.updateVisibility(!this.fileTreeIsVisible);
|
||||||
|
},
|
||||||
|
updateVisibility(visible) {
|
||||||
|
this.fileTreeIsVisible = visible;
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_KEY, this.fileTreeIsVisible);
|
||||||
|
this.adjustToggleButton(this.fileTreeIsVisible);
|
||||||
|
},
|
||||||
|
adjustToggleButton(visible) {
|
||||||
|
const [toShow, toHide] = document.querySelectorAll('.diff-toggle-file-tree-button .icon');
|
||||||
|
toShow.classList.toggle('hide', visible); // hide the toShow icon if the tree is visible
|
||||||
|
toHide.classList.toggle('hide', !visible); // similarly
|
||||||
|
},
|
||||||
|
loadMoreData() {
|
||||||
|
this.isLoadingNewData = true;
|
||||||
|
doLoadMoreFiles(this.link, this.diffEnd, () => {
|
||||||
|
this.isLoadingNewData = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
150
web_src/js/components/DiffFileTreeItem.vue
Normal file
150
web_src/js/components/DiffFileTreeItem.vue
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<template>
|
||||||
|
<div v-show="show">
|
||||||
|
<div class="item" :class="item.isFile ? 'filewrapper p-1' : ''">
|
||||||
|
<!-- Files -->
|
||||||
|
<SvgIcon
|
||||||
|
v-if="item.isFile"
|
||||||
|
data-position="right center"
|
||||||
|
name="octicon-file"
|
||||||
|
class="svg-icon file"
|
||||||
|
/>
|
||||||
|
<a
|
||||||
|
v-if="item.isFile"
|
||||||
|
class="file ellipsis"
|
||||||
|
:href="item.isFile ? '#diff-' + item.file.NameHash : ''"
|
||||||
|
>{{ item.name }}</a>
|
||||||
|
<SvgIcon
|
||||||
|
v-if="item.isFile"
|
||||||
|
data-position="right center"
|
||||||
|
:name="getIconForDiffType(item.file.Type)"
|
||||||
|
:class="['svg-icon', getIconForDiffType(item.file.Type), 'status']"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Directories -->
|
||||||
|
<div v-if="!item.isFile" class="directory p-1" @click.stop="handleClick(item.isFile)">
|
||||||
|
<SvgIcon
|
||||||
|
class="svg-icon"
|
||||||
|
:name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"
|
||||||
|
/>
|
||||||
|
<SvgIcon
|
||||||
|
class="svg-icon directory"
|
||||||
|
name="octicon-file-directory-fill"
|
||||||
|
/>
|
||||||
|
<span class="ellipsis">{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-show="!collapsed">
|
||||||
|
<DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem" class="list" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {SvgIcon} from '../svg.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DiffFileTreeItem',
|
||||||
|
components: {
|
||||||
|
SvgIcon,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
collapsed: false,
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
handleClick(itemIsFile) {
|
||||||
|
if (itemIsFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$set(this, 'collapsed', !this.collapsed);
|
||||||
|
},
|
||||||
|
getIconForDiffType(pType) {
|
||||||
|
const diffTypes = {
|
||||||
|
1: 'octicon-diff-added',
|
||||||
|
2: 'octicon-diff-modified',
|
||||||
|
3: 'octicon-diff-removed',
|
||||||
|
4: 'octicon-diff-renamed',
|
||||||
|
5: 'octicon-diff-modified', // there is no octicon for copied, so modified should be ok
|
||||||
|
};
|
||||||
|
return diffTypes[pType];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
span.svg-icon.status {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
span.svg-icon.file {
|
||||||
|
color: var(--color-secondary-dark-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.svg-icon.directory {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.svg-icon.octicon-diff-modified {
|
||||||
|
color: var(--color-yellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.svg-icon.octicon-diff-added {
|
||||||
|
color: var(--color-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.svg-icon.octicon-diff-removed {
|
||||||
|
color: var(--color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.svg-icon.octicon-diff-renamed {
|
||||||
|
color: var(--color-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item.filewrapper {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: 20px 7fr 1fr;
|
||||||
|
padding-left: 18px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item.filewrapper:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-hover);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.directory {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 18px 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.directory:hover {
|
||||||
|
color: var(--color-text);
|
||||||
|
background: var(--color-hover);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.list {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
padding-top: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
21
web_src/js/features/repo-diff-filetree.js
Normal file
21
web_src/js/features/repo-diff-filetree.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import DiffFileTree from '../components/DiffFileTree.vue';
|
||||||
|
import DiffFileList from '../components/DiffFileList.vue';
|
||||||
|
|
||||||
|
export default function initDiffFileTree() {
|
||||||
|
const el = document.getElementById('diff-file-tree-container');
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const View = Vue.extend({
|
||||||
|
render: (createElement) => createElement(DiffFileTree),
|
||||||
|
});
|
||||||
|
new View().$mount(el);
|
||||||
|
|
||||||
|
const fileListElement = document.getElementById('diff-file-list-container');
|
||||||
|
if (!fileListElement) return;
|
||||||
|
|
||||||
|
const fileListView = Vue.extend({
|
||||||
|
render: (createElement) => createElement(DiffFileList),
|
||||||
|
});
|
||||||
|
new fileListView().$mount(fileListElement);
|
||||||
|
}
|
|
@ -68,7 +68,6 @@ export function initRepoDiffConversationForm() {
|
||||||
initCompReactionSelector($newConversationHolder);
|
initCompReactionSelector($newConversationHolder);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$(document).on('click', '.resolve-conversation', async function (e) {
|
$(document).on('click', '.resolve-conversation', async function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const comment_id = $(this).data('comment-id');
|
const comment_id = $(this).data('comment-id');
|
||||||
|
@ -118,32 +117,27 @@ function onShowMoreFiles() {
|
||||||
countAndUpdateViewedFiles();
|
countAndUpdateViewedFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initRepoDiffShowMore() {
|
export function doLoadMoreFiles(link, diffEnd, callback) {
|
||||||
$('#diff-files, #diff-file-boxes').on('click', '#diff-show-more-files, #diff-show-more-files-stats', (e) => {
|
const url = `${link}?skip-to=${diffEnd}&file-only=true`;
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if ($(e.target).hasClass('disabled')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$('#diff-show-more-files, #diff-show-more-files-stats').addClass('disabled');
|
|
||||||
|
|
||||||
const url = $('#diff-show-more-files, #diff-show-more-files-stats').data('href');
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url,
|
url,
|
||||||
}).done((resp) => {
|
}).done((resp) => {
|
||||||
if (!resp) {
|
if (!resp) {
|
||||||
$('#diff-show-more-files, #diff-show-more-files-stats').removeClass('disabled');
|
callback(resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$('#diff-too-many-files-stats').remove();
|
// By simply rerunning the script we add the new data to our existing
|
||||||
$('#diff-files').append($(resp).find('#diff-files li'));
|
// pagedata object. this triggers vue and the filetree and filelist will
|
||||||
$('#diff-incomplete').replaceWith($(resp).find('#diff-file-boxes').children());
|
// render the new elements.
|
||||||
onShowMoreFiles();
|
$('body').append($(resp).find('script#diff-data-script'));
|
||||||
|
callback(resp);
|
||||||
}).fail(() => {
|
}).fail(() => {
|
||||||
$('#diff-show-more-files, #diff-show-more-files-stats').removeClass('disabled');
|
callback();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initRepoDiffShowMore() {
|
||||||
$(document).on('click', 'a.diff-show-more-button', (e) => {
|
$(document).on('click', 'a.diff-show-more-button', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const $target = $(e.target);
|
const $target = $(e.target);
|
||||||
|
@ -163,7 +157,6 @@ export function initRepoDiffShowMore() {
|
||||||
$target.removeClass('disabled');
|
$target.removeClass('disabled');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$target.parent().replaceWith($(resp).find('#diff-file-boxes .diff-file-body .file-body').children());
|
$target.parent().replaceWith($(resp).find('#diff-file-boxes .diff-file-body .file-body').children());
|
||||||
onShowMoreFiles();
|
onShowMoreFiles();
|
||||||
}).fail(() => {
|
}).fail(() => {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {initRepoIssueContentHistory} from './features/repo-issue-content.js';
|
||||||
import {initStopwatch} from './features/stopwatch.js';
|
import {initStopwatch} from './features/stopwatch.js';
|
||||||
import {initFindFileInRepo} from './features/repo-findfile.js';
|
import {initFindFileInRepo} from './features/repo-findfile.js';
|
||||||
import {initCommentContent, initMarkupContent} from './markup/content.js';
|
import {initCommentContent, initMarkupContent} from './markup/content.js';
|
||||||
|
import initDiffFileTree from './features/repo-diff-filetree.js';
|
||||||
|
|
||||||
import {initUserAuthLinkAccountView, initUserAuthOauth2} from './features/user-auth.js';
|
import {initUserAuthLinkAccountView, initUserAuthOauth2} from './features/user-auth.js';
|
||||||
import {
|
import {
|
||||||
|
@ -158,6 +159,7 @@ $(document).ready(() => {
|
||||||
initRepoDiffFileViewToggle();
|
initRepoDiffFileViewToggle();
|
||||||
initRepoDiffReviewButton();
|
initRepoDiffReviewButton();
|
||||||
initRepoDiffShowMore();
|
initRepoDiffShowMore();
|
||||||
|
initDiffFileTree();
|
||||||
initRepoEditor();
|
initRepoEditor();
|
||||||
initRepoGraphGit();
|
initRepoGraphGit();
|
||||||
initRepoIssueContentHistory();
|
initRepoIssueContentHistory();
|
||||||
|
|
|
@ -2,6 +2,11 @@ import octiconChevronDown from '../../public/img/svg/octicon-chevron-down.svg';
|
||||||
import octiconChevronRight from '../../public/img/svg/octicon-chevron-right.svg';
|
import octiconChevronRight from '../../public/img/svg/octicon-chevron-right.svg';
|
||||||
import octiconCopy from '../../public/img/svg/octicon-copy.svg';
|
import octiconCopy from '../../public/img/svg/octicon-copy.svg';
|
||||||
import octiconClock from '../../public/img/svg/octicon-clock.svg';
|
import octiconClock from '../../public/img/svg/octicon-clock.svg';
|
||||||
|
import octiconDiffAdded from '../../public/img/svg/octicon-diff-added.svg';
|
||||||
|
import octiconDiffModified from '../../public/img/svg/octicon-diff-modified.svg';
|
||||||
|
import octiconDiffRemoved from '../../public/img/svg/octicon-diff-removed.svg';
|
||||||
|
import octiconDiffRenamed from '../../public/img/svg/octicon-diff-renamed.svg';
|
||||||
|
import octiconFileDirectoryFill from '../../public/img/svg/octicon-file-directory-fill.svg';
|
||||||
import octiconGitMerge from '../../public/img/svg/octicon-git-merge.svg';
|
import octiconGitMerge from '../../public/img/svg/octicon-git-merge.svg';
|
||||||
import octiconGitPullRequest from '../../public/img/svg/octicon-git-pull-request.svg';
|
import octiconGitPullRequest from '../../public/img/svg/octicon-git-pull-request.svg';
|
||||||
import octiconIssueClosed from '../../public/img/svg/octicon-issue-closed.svg';
|
import octiconIssueClosed from '../../public/img/svg/octicon-issue-closed.svg';
|
||||||
|
@ -17,6 +22,9 @@ import octiconRepoForked from '../../public/img/svg/octicon-repo-forked.svg';
|
||||||
import octiconRepoTemplate from '../../public/img/svg/octicon-repo-template.svg';
|
import octiconRepoTemplate from '../../public/img/svg/octicon-repo-template.svg';
|
||||||
import octiconTriangleDown from '../../public/img/svg/octicon-triangle-down.svg';
|
import octiconTriangleDown from '../../public/img/svg/octicon-triangle-down.svg';
|
||||||
import octiconFile from '../../public/img/svg/octicon-file.svg';
|
import octiconFile from '../../public/img/svg/octicon-file.svg';
|
||||||
|
import octiconSidebarExpand from '../../public/img/svg/octicon-sidebar-expand.svg';
|
||||||
|
import octiconSidebarCollapse from '../../public/img/svg/octicon-sidebar-collapse.svg';
|
||||||
|
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
@ -40,8 +48,16 @@ export const svgs = {
|
||||||
'octicon-repo-template': octiconRepoTemplate,
|
'octicon-repo-template': octiconRepoTemplate,
|
||||||
'octicon-triangle-down': octiconTriangleDown,
|
'octicon-triangle-down': octiconTriangleDown,
|
||||||
'octicon-file': octiconFile,
|
'octicon-file': octiconFile,
|
||||||
|
'octicon-file-directory-fill': octiconFileDirectoryFill,
|
||||||
|
'octicon-sidebar-expand': octiconSidebarExpand,
|
||||||
|
'octicon-sidebar-collapse': octiconSidebarCollapse,
|
||||||
|
'octicon-diff-added': octiconDiffAdded,
|
||||||
|
'octicon-diff-modified': octiconDiffModified,
|
||||||
|
'octicon-diff-removed': octiconDiffRemoved,
|
||||||
|
'octicon-diff-renamed': octiconDiffRenamed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const serializer = new XMLSerializer();
|
const serializer = new XMLSerializer();
|
||||||
|
|
||||||
|
|
|
@ -3068,6 +3068,35 @@ td.blob-excerpt {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#diff-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#diff-file-boxes {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diff-file-tree {
|
||||||
|
width: 20%;
|
||||||
|
max-width: 380px;
|
||||||
|
line-height: inherit;
|
||||||
|
position: sticky;
|
||||||
|
padding-top: 0;
|
||||||
|
top: 47px;
|
||||||
|
max-height: calc(100vh - 50px);
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media @mediaMdAndDown {
|
||||||
|
#diff-file-tree {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-toggle-file-tree-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ui.message.unicode-escape-prompt {
|
.ui.message.unicode-escape-prompt {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
|
Loading…
Reference in a new issue