diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ddc12aefaaaf9..40c68ecec4bb6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -111,6 +111,8 @@ copy_error = Copy failed copy_type_unsupported = This file type cannot be copied copy_filename = Copy filename +repo.more_operations = More Operations + write = Write preview = Preview loading = Loading… @@ -1317,6 +1319,7 @@ ambiguous_character = `%[1]c [U+%04[1]X] can be confused with %[2]c [U+%04[2]X]` escape_control_characters = Escape unescape_control_characters = Unescape file_copy_permalink = Copy Permalink +center_content = Center content view_git_blame = View Git Blame video_not_supported_in_browser = Your browser does not support the HTML5 'video' tag. audio_not_supported_in_browser = Your browser does not support the HTML5 'audio' tag. @@ -1354,8 +1357,11 @@ editor.this_file_locked = File is locked editor.must_be_on_a_branch = You must be on a branch to make or propose changes to this file. editor.fork_before_edit = You must fork this repository to make or propose changes to this file. editor.delete_this_file = Delete File +editor.delete_this_directory = Delete Directory editor.must_have_write_access = You must have write access to make or propose changes to this file. editor.file_delete_success = File "%s" has been deleted. +editor.directory_delete_success = Directory "%s" has been deleted. +editor.delete_directory = Delete directory '%s' editor.name_your_file = Name your file… editor.filename_help = Add a directory by typing its name followed by a slash ('/'). Remove a directory by typing backspace at the beginning of the input field. editor.or = or diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 8c630cb35f0a6..bf664146fa45b 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -280,6 +280,8 @@ func EditFile(ctx *context.Context) { return } + prepareHomeTreeSideBarSwitch(ctx) + // on the "New File" page, we should add an empty path field to make end users could input a new name prepareTreePathFieldsAndPaths(ctx, util.Iif(isNewFile, ctx.Repo.TreePath+"/", ctx.Repo.TreePath)) @@ -384,7 +386,7 @@ func DeleteFile(ctx *context.Context) { ctx.HTML(http.StatusOK, tplDeleteFile) } -// DeleteFilePost response for deleting file +// DeleteFilePost response for deleting file or directory func DeleteFilePost(ctx *context.Context) { parsed := prepareEditorCommitSubmittedForm[*forms.DeleteRepoFileForm](ctx) if ctx.Written() { @@ -392,33 +394,80 @@ func DeleteFilePost(ctx *context.Context) { } treePath := ctx.Repo.TreePath - _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{ - LastCommitID: parsed.form.LastCommit, - OldBranch: parsed.OldBranchName, - NewBranch: parsed.NewBranchName, - Files: []*files_service.ChangeRepoFile{ + + // Check if the path is a directory + entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treePath) + if err != nil { + ctx.NotFoundOrServerError("GetTreeEntryByPath", git.IsErrNotExist, err) + return + } + + var filesToDelete []*files_service.ChangeRepoFile + var commitMessage string + + if entry.IsDir() { + // Get all files in the directory recursively + tree, err := ctx.Repo.Commit.SubTree(treePath) + if err != nil { + ctx.ServerError("SubTree", err) + return + } + + entries, err := tree.ListEntriesRecursiveFast() + if err != nil { + ctx.ServerError("ListEntriesRecursiveFast", err) + return + } + + // Create delete operations for all files in the directory + for _, e := range entries { + if !e.IsDir() && !e.IsSubModule() { + filesToDelete = append(filesToDelete, &files_service.ChangeRepoFile{ + Operation: "delete", + TreePath: treePath + "/" + e.Name(), + }) + } + } + + commitMessage = parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete_directory", treePath)) + } else { + // Single file deletion + filesToDelete = []*files_service.ChangeRepoFile{ { Operation: "delete", TreePath: treePath, }, - }, - Message: parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete", treePath)), - Signoff: parsed.form.Signoff, - Author: parsed.GitCommitter, - Committer: parsed.GitCommitter, + } + commitMessage = parsed.GetCommitMessage(ctx.Locale.TrString("repo.editor.delete", treePath)) + } + + _, err = files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{ + LastCommitID: parsed.form.LastCommit, + OldBranch: parsed.OldBranchName, + NewBranch: parsed.NewBranchName, + Files: filesToDelete, + Message: commitMessage, + Signoff: parsed.form.Signoff, + Author: parsed.GitCommitter, + Committer: parsed.GitCommitter, }) if err != nil { editorHandleFileOperationError(ctx, parsed.NewBranchName, err) return } - ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", treePath)) + if entry.IsDir() { + ctx.Flash.Success(ctx.Tr("repo.editor.directory_delete_success", treePath)) + } else { + ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", treePath)) + } redirectTreePath := getClosestParentWithFiles(ctx.Repo.GitRepo, parsed.NewBranchName, treePath) redirectForCommitChoice(ctx, parsed, redirectTreePath) } func UploadFile(ctx *context.Context) { ctx.Data["PageIsUpload"] = true + prepareHomeTreeSideBarSwitch(ctx) prepareTreePathFieldsAndPaths(ctx, ctx.Repo.TreePath) opts := prepareEditorCommitFormOptions(ctx, "_upload") if ctx.Written() { diff --git a/routers/web/repo/editor_apply_patch.go b/routers/web/repo/editor_apply_patch.go index aad7b4129c795..a18369e5a47be 100644 --- a/routers/web/repo/editor_apply_patch.go +++ b/routers/web/repo/editor_apply_patch.go @@ -14,6 +14,7 @@ import ( ) func NewDiffPatch(ctx *context.Context) { + prepareHomeTreeSideBarSwitch(ctx) prepareEditorCommitFormOptions(ctx, "_diffpatch") if ctx.Written() { return diff --git a/routers/web/repo/view_file.go b/routers/web/repo/view_file.go index ea3920439d426..e37d0a576acfd 100644 --- a/routers/web/repo/view_file.go +++ b/routers/web/repo/view_file.go @@ -306,5 +306,11 @@ func prepareFileViewEditorButtons(ctx *context.Context) bool { ctx.Data["EditFileTooltip"] = util.Iif(isLFSLocked, ctx.Tr("repo.editor.this_file_locked"), ctx.Tr("repo.editor.edit_this_file")) ctx.Data["CanDeleteFile"] = !isLFSLocked ctx.Data["DeleteFileTooltip"] = util.Iif(isLFSLocked, ctx.Tr("repo.editor.this_file_locked"), ctx.Tr("repo.editor.delete_this_file")) + + // Generate unique branch name for delete modal + if ctx.Doer != nil { + ctx.Data["new_branch_name"] = getUniquePatchBranchName(ctx, ctx.Doer.LowerName, ctx.Repo.Repository) + } + return true } diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index 10872f8af7452..f39009f2183ad 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -1,4 +1,7 @@
+ {{ctx.AvatarUtils.Avatar .SignedUser 40 "commit-avatar"}}

@@ -80,8 +83,9 @@ {{end}}

- - {{ctx.Locale.Tr "repo.editor.cancel"}} +
+ +
diff --git a/templates/repo/editor/common_breadcrumb.tmpl b/templates/repo/editor/common_breadcrumb.tmpl index 8cfbe09d3eef5..e91648d6129e6 100644 --- a/templates/repo/editor/common_breadcrumb.tmpl +++ b/templates/repo/editor/common_breadcrumb.tmpl @@ -11,6 +11,5 @@ {{$v}} {{end}} {{end}} - {{ctx.Locale.Tr "repo.editor.or"}} {{ctx.Locale.Tr "repo.editor.cancel_lower"}} diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 0911d02e1f423..21306c85aaf2d 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -1,16 +1,34 @@ {{template "base/head" .}}
{{template "repo/header" .}} -
+
{{template "base/alert" .}} -
+
+ {{template "repo/view_file_tree" .}} +
+
+ {{.CsrfTokenHtml}} {{template "repo/editor/common_top" .}} -
- {{template "repo/editor/common_breadcrumb" .}} +
+
+ + {{template "repo/editor/common_breadcrumb" .}} +
+
+ {{ctx.Locale.Tr "repo.editor.cancel"}} + +
{{if not .NotEditableReason}}
@@ -46,8 +64,22 @@
{{end}} - {{template "repo/editor/commit_form" .}} - + {{/* Commit form fields - inside form but hidden, will be shown in modal */}} + + {{/* Hidden dummy button for repo-editor.ts compatibility */}} + + +
+
+
+ + {{/* Commit Changes Modal - fields will be moved here visually */}} +
{{template "base/footer" .}} diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index fa00edd92e77c..72c23e30507fe 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -1,23 +1,38 @@ {{template "base/head" .}}
{{template "repo/header" .}} -
+
{{template "base/alert" .}} -
+
+ {{template "repo/view_file_tree" .}} +
+
+ {{.CsrfTokenHtml}} {{template "repo/editor/common_top" .}} -
- - {{template "repo/editor/commit_form" .}} - + {{/* Commit form fields - inside form but hidden, will be shown in modal */}} + + {{/* Hidden dummy button for repo-editor.ts compatibility */}} + + +
+
+
+ + {{/* Commit Changes Modal - fields will be moved here visually */}} +
{{template "base/footer" .}} diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl index 3e36c77b3b924..b983f4a6516eb 100644 --- a/templates/repo/editor/upload.tmpl +++ b/templates/repo/editor/upload.tmpl @@ -1,19 +1,51 @@ {{template "base/head" .}}
{{template "repo/header" .}} -
+
{{template "base/alert" .}} -
+
+
+ {{template "repo/view_file_tree" .}} +
+
+ {{.CsrfTokenHtml}} {{template "repo/editor/common_top" .}} -
- {{template "repo/editor/common_breadcrumb" .}} +
+
+ + {{template "repo/editor/common_breadcrumb" .}} +
+
+ {{ctx.Locale.Tr "repo.editor.cancel"}} + +
{{template "repo/upload" .}}
- {{template "repo/editor/commit_form" .}} - + {{/* Commit form fields - inside form but hidden, will be shown in modal */}} + + {{/* Hidden dummy button for repo-editor.ts compatibility */}} + + +
+
+
+ + {{/* Commit Changes Modal - fields will be moved here visually */}} +
{{template "base/footer" .}} diff --git a/templates/repo/view_content.tmpl b/templates/repo/view_content.tmpl index 66e4fffcb9b19..04a16543d624b 100644 --- a/templates/repo/view_content.tmpl +++ b/templates/repo/view_content.tmpl @@ -42,26 +42,6 @@ {{ctx.Locale.Tr "repo.find_file.go_to_file"}} {{end}} - {{if and .RefFullName.IsBranch (not .IsViewFile)}} - - {{end}} - {{if and $isTreePathRoot .Repository.IsTemplate}} {{ctx.Locale.Tr "repo.use_template"}} @@ -86,6 +66,52 @@
+ {{if .RefFullName.IsBranch}} + {{$addFilePath := .TreePath}} + {{if .IsViewFile}} + {{if gt (len .TreeNames) 1}} + {{$addFilePath = StringUtils.Join (slice .TreeNames 0 (Eval (len .TreeNames) "-" 1)) "/"}} + {{else}} + {{$addFilePath = ""}} + {{end}} + {{end}} + + {{if not .IsViewFile}} + + {{end}} + {{end}} {{if $isTreePathRoot}} {{template "repo/clone_panel" .}} diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 8fce1b6f2c8fc..a02c282dc6eb7 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -74,7 +74,7 @@ {{svg "octicon-pencil"}} {{end}} {{if .CanDeleteFile}} - {{svg "octicon-trash"}} + {{svg "octicon-trash"}} {{else}} {{svg "octicon-trash"}} {{end}} @@ -148,4 +148,75 @@ {{ctx.Locale.Tr "repo.file_copy_permalink"}}
+ + {{/* Delete File Modal */}} + {{if .CanDeleteFile}} + + {{end}}
diff --git a/templates/repo/view_file_tree.tmpl b/templates/repo/view_file_tree.tmpl index 8aed05f346940..29302599ef508 100644 --- a/templates/repo/view_file_tree.tmpl +++ b/templates/repo/view_file_tree.tmpl @@ -7,9 +7,14 @@ {{ctx.Locale.Tr "files"}} +
+ +
+ {{/* TODO: Dynamically move components such as refSelector and createPR here */}} -
diff --git a/web_src/css/index.css b/web_src/css/index.css index 291cd04b2b95c..c4d3a22157520 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -63,6 +63,9 @@ @import "./repo/issue-list.css"; @import "./repo/list-header.css"; @import "./repo/file-view.css"; +@import "./repo/file-actions.css"; +@import "./repo/editor-commit.css"; +@import "./repo/delete-file.css"; @import "./repo/wiki.css"; @import "./repo/header.css"; @import "./repo/home.css"; diff --git a/web_src/css/repo/delete-file.css b/web_src/css/repo/delete-file.css new file mode 100644 index 0000000000000..be211434ce53f --- /dev/null +++ b/web_src/css/repo/delete-file.css @@ -0,0 +1,92 @@ +/* Delete file modal styling */ +#delete-file-modal { + max-width: 680px; +} + +#delete-file-modal .content { + padding: 1.5rem; +} + +#delete-file-modal .commit-form-wrapper { + padding-left: 48px; + position: relative; +} + +#delete-file-modal .commit-form-wrapper .commit-avatar { + float: left; + margin-left: -48px; +} + +#delete-file-modal .commit-form-wrapper .commit-form { + position: relative; + padding: 15px; + border: 1px solid var(--color-secondary); + border-radius: var(--border-radius); + background-color: var(--color-box-body); +} + +#delete-file-modal .commit-form h3 { + margin-top: 0; + margin-bottom: 1rem; +} + +#delete-file-modal .commit-form .field { + margin-bottom: 1rem; +} + +#delete-file-modal .commit-form input[name="commit_summary"] { + width: 100%; + padding: 10px 12px; + font-size: 14px; +} + +#delete-file-modal .commit-form textarea[name="commit_message"] { + width: 100%; + padding: 10px 12px; + font-size: 14px; +} + +#delete-file-modal .quick-pull-choice .field { + margin-bottom: 0.75rem; +} + +#delete-file-modal .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input { + position: relative; + margin-left: 25px; +} + +#delete-file-modal .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input { + width: 240px !important; + padding-left: 26px !important; +} + +#delete-file-modal .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch { + position: absolute; + top: 9px; + left: 8px; +} + +/* Arrow pointing to avatar */ +#delete-file-modal .avatar-content-left-arrow::before, +#delete-file-modal .avatar-content-left-arrow::after { + right: 100%; + top: 20px; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +#delete-file-modal .avatar-content-left-arrow::before { + border-right-color: var(--color-secondary); + border-width: 9px; + margin-top: -9px; +} + +#delete-file-modal .avatar-content-left-arrow::after { + border-right-color: var(--color-box-body); + border-width: 8px; + margin-top: -8px; +} diff --git a/web_src/css/repo/editor-commit.css b/web_src/css/repo/editor-commit.css new file mode 100644 index 0000000000000..281e7719d11c5 --- /dev/null +++ b/web_src/css/repo/editor-commit.css @@ -0,0 +1,129 @@ +/* Editor commit modal styling */ +#commit-changes-modal .content { + padding: 1.5rem; +} + +.commit-form-in-modal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10001; + max-width: 680px; + width: 90%; + max-height: 85vh; + overflow-y: auto; + background: var(--color-box-body); + border-radius: 6px; + padding: 1.5rem; + padding-top: 3rem; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +/* Close button inside the commit form */ +.commit-modal-close { + position: absolute; + top: 0.5rem; + right: 0.5rem; + cursor: pointer; + background: transparent; + border: none; + padding: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text); + opacity: 0.6; + z-index: 10; + border-radius: 4px; + transition: all 0.2s; +} + +.commit-modal-close:hover { + opacity: 1; + background: var(--color-hover); +} + +#commit-changes-modal .commit-form-wrapper { + padding-left: 48px; + position: relative; +} + +#commit-changes-modal .commit-form-wrapper .commit-avatar { + float: left; + margin-left: -48px; +} + +#commit-changes-modal .commit-form-wrapper .commit-form { + position: relative; + padding: 15px; + border: 1px solid var(--color-secondary); + border-radius: var(--border-radius); + background-color: var(--color-box-body); +} + +#commit-changes-modal .commit-form h3 { + margin-top: 0; + margin-bottom: 1rem; +} + +#commit-changes-modal .commit-form .field { + margin-bottom: 1rem; +} + +#commit-changes-modal .commit-form input[name="commit_summary"] { + width: 100%; + padding: 10px 12px; + font-size: 14px; +} + +#commit-changes-modal .commit-form textarea[name="commit_message"] { + width: 100%; + padding: 10px 12px; + font-size: 14px; +} + +#commit-changes-modal .quick-pull-choice .field { + margin-bottom: 0.75rem; +} + +#commit-changes-modal .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input { + position: relative; + margin-left: 25px; +} + +#commit-changes-modal .commit-form-wrapper .commit-form .quick-pull-choice .new-branch-name-input input { + width: 240px !important; + padding-left: 26px !important; +} + +#commit-changes-modal .commit-form-wrapper .commit-form .quick-pull-choice .octicon-git-branch { + position: absolute; + top: 9px; + left: 8px; +} + +/* Arrow pointing to avatar */ +#commit-changes-modal .avatar-content-left-arrow::before, +#commit-changes-modal .avatar-content-left-arrow::after { + right: 100%; + top: 20px; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} + +#commit-changes-modal .avatar-content-left-arrow::before { + border-right-color: var(--color-secondary); + border-width: 9px; + margin-top: -9px; +} + +#commit-changes-modal .avatar-content-left-arrow::after { + border-right-color: var(--color-box-body); + border-width: 8px; + margin-top: -8px; +} diff --git a/web_src/css/repo/file-actions.css b/web_src/css/repo/file-actions.css new file mode 100644 index 0000000000000..403131aa56c72 --- /dev/null +++ b/web_src/css/repo/file-actions.css @@ -0,0 +1,28 @@ +/* Repository file actions dropdown and centered content */ +.ui.dropdown.repo-add-file > .menu { + margin-top: 4px !important; +} + +.ui.dropdown.repo-file-actions-dropdown > .menu { + margin-top: 4px !important; + min-width: 200px; +} + +.repo-file-actions-dropdown .menu .item { + cursor: pointer; +} + +.repo-file-actions-dropdown .menu .divider { + margin: 0.5rem 0; +} + +.ui.dropdown.repo-file-actions-dropdown > .menu > .item.danger, +.ui.dropdown.repo-file-actions-dropdown > .menu > .item.danger svg { + color: var(--color-red) !important; +} + +.ui.dropdown.repo-file-actions-dropdown > .menu > .item.danger:hover, +.ui.dropdown.repo-file-actions-dropdown > .menu > .item.danger:hover svg { + color: var(--color-red) !important; + background: var(--color-red-badge-hover-bg) !important; +} diff --git a/web_src/css/repo/home.css b/web_src/css/repo/home.css index ee371f1b1c982..f0c366c7650fa 100644 --- a/web_src/css/repo/home.css +++ b/web_src/css/repo/home.css @@ -63,6 +63,8 @@ bottom: 0; height: 100%; overflow-y: hidden; + overflow-x: visible; + z-index: 10; } .repo-view-content { diff --git a/web_src/js/components/ViewFileTree.vue b/web_src/js/components/ViewFileTree.vue index 1f90f9258670c..d90212fb6777a 100644 --- a/web_src/js/components/ViewFileTree.vue +++ b/web_src/js/components/ViewFileTree.vue @@ -1,9 +1,17 @@ diff --git a/web_src/js/features/repo-delete-file.ts b/web_src/js/features/repo-delete-file.ts new file mode 100644 index 0000000000000..08435966c686c --- /dev/null +++ b/web_src/js/features/repo-delete-file.ts @@ -0,0 +1,42 @@ +import $ from 'jquery'; + +export function initRepoDeleteFile() { + const deleteButton = document.querySelector('#delete-file-button'); + const deleteModal = document.querySelector('#delete-file-modal'); + const deleteForm = document.querySelector('#delete-file-form'); + + if (!deleteButton || !deleteModal || !deleteForm) { + return; + } + + deleteButton.addEventListener('click', (e) => { + e.preventDefault(); + $(deleteModal).modal('show'); + }); + + // Handle form submission + deleteForm.addEventListener('submit', () => { + $(deleteModal).modal('hide'); + }); + + // Handle commit choice radio buttons + const commitChoiceRadios = deleteForm.querySelectorAll('input[name="commit_choice"]'); + const newBranchNameContainer = deleteForm.querySelector('.quick-pull-branch-name'); + const newBranchNameInput = deleteForm.querySelector('input[name="new_branch_name"]'); + + for (const radio of commitChoiceRadios) { + radio.addEventListener('change', () => { + if (radio.value === 'commit-to-new-branch') { + newBranchNameContainer?.classList.remove('tw-hidden'); + if (newBranchNameInput) { + newBranchNameInput.required = true; + } + } else { + newBranchNameContainer?.classList.add('tw-hidden'); + if (newBranchNameInput) { + newBranchNameInput.required = false; + } + } + }); + } +} diff --git a/web_src/js/features/repo-editor-commit.ts b/web_src/js/features/repo-editor-commit.ts new file mode 100644 index 0000000000000..82b561cc1510b --- /dev/null +++ b/web_src/js/features/repo-editor-commit.ts @@ -0,0 +1,102 @@ +import $ from 'jquery'; + +export function initRepoEditorCommit() { + const commitButton = document.querySelector('#commit-changes-button'); + const commitModal = document.querySelector('#commit-changes-modal'); + const modalCommitButton = document.querySelector('#commit-button'); + + if (!commitButton || !commitModal) return; + + const elForm = document.querySelector('.repository.editor .edit.form'); + const dirtyFileClass = 'dirty-file'; + + // Sync the top commit button state with the modal commit button state + const syncTopCommitButtonState = () => { + // Check if form has changes (using the same dirty class from repo-editor.ts) + const hasChanges = elForm?.classList.contains(dirtyFileClass); + + // Also check the modal commit button state as fallback + const modalButtonDisabled = modalCommitButton?.disabled; + + if (hasChanges || !modalButtonDisabled) { + commitButton.classList.remove('disabled'); + } else { + commitButton.classList.add('disabled'); + } + }; + + // For upload page - enable button when files are added + const dropzone = document.querySelector('.dropzone'); + if (dropzone) { + const observer = new MutationObserver(() => { + const filesContainer = dropzone.querySelector('.files'); + const hasFiles = filesContainer && filesContainer.children.length > 0; + + if (hasFiles) { + commitButton.classList.remove('disabled'); + } else { + commitButton.classList.add('disabled'); + } + }); + + const filesContainer = dropzone.querySelector('.files'); + if (filesContainer) { + observer.observe(filesContainer, {childList: true}); + } + } + + // Watch for changes in the form's dirty state + if (elForm) { + const observer = new MutationObserver(syncTopCommitButtonState); + observer.observe(elForm, {attributes: true, attributeFilter: ['class']}); + + // Initial sync + syncTopCommitButtonState(); + } + + // Also sync when modal commit button state changes + if (modalCommitButton) { + const observer = new MutationObserver(syncTopCommitButtonState); + observer.observe(modalCommitButton, {attributes: true, attributeFilter: ['disabled']}); + } + + const commitFormFields = document.querySelector('#commit-form-fields'); + + commitButton.addEventListener('click', (e) => { + e.preventDefault(); + if (!commitButton.classList.contains('disabled')) { + // Show the commit form fields (they stay in the form, just become visible) + if (commitFormFields) { + commitFormFields.style.display = 'block'; + // Position it inside the modal using CSS + commitFormFields.classList.add('commit-form-in-modal'); + } + $(commitModal).modal('show'); + } + }); + + // When modal closes, hide the form fields again + $(commitModal).modal({ + onHidden: () => { + if (commitFormFields) { + commitFormFields.style.display = 'none'; + commitFormFields.classList.remove('commit-form-in-modal'); + } + }, + }); + + // Handle close button click + const closeButton = document.querySelector('#commit-modal-close-btn'); + if (closeButton) { + closeButton.addEventListener('click', () => { + $(commitModal).modal('hide'); + }); + } + + // Handle form submission - close modal after submit + if (elForm) { + elForm.addEventListener('submit', () => { + $(commitModal).modal('hide'); + }); + } +} diff --git a/web_src/js/index-domready.ts b/web_src/js/index-domready.ts index df56c85c868c6..f44a84476009b 100644 --- a/web_src/js/index-domready.ts +++ b/web_src/js/index-domready.ts @@ -34,6 +34,8 @@ import {initOrgTeam} from './features/org-team.ts'; import {initUserAuthWebAuthn, initUserAuthWebAuthnRegister} from './features/user-auth-webauthn.ts'; import {initRepoRelease, initRepoReleaseNew} from './features/repo-release.ts'; import {initRepoEditor} from './features/repo-editor.ts'; +import {initRepoEditorCommit} from './features/repo-editor-commit.ts'; +import {initRepoDeleteFile} from './features/repo-delete-file.ts'; import {initCompSearchUserBox} from './features/comp/SearchUserBox.ts'; import {initInstall} from './features/install.ts'; import {initCompWebHookEditor} from './features/comp/WebHookEditor.ts'; @@ -123,6 +125,8 @@ const initPerformanceTracer = callInitFunctions([ initRepoEllipsisButton, initRepoDiffCommitBranchesAndTags, initRepoEditor, + initRepoEditorCommit, + initRepoDeleteFile, initRepoGraphGit, initRepoIssueContentHistory, initRepoIssueList,