diff --git a/app/controllers/work_packages/hover_card_controller.rb b/app/controllers/work_packages/hover_card_controller.rb
new file mode 100644
index 00000000000..7979c7821fd
--- /dev/null
+++ b/app/controllers/work_packages/hover_card_controller.rb
@@ -0,0 +1,37 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+module WorkPackages
+ class HoverCardController < ApplicationController
+ before_action :load_and_authorize_in_optional_project
+
+ def show
+ @id = params[:id]
+ render layout: nil
+ end
+ end
+end
diff --git a/app/helpers/work_packages_helper.rb b/app/helpers/work_packages_helper.rb
index 8e237f03638..eb320e18141 100644
--- a/app/helpers/work_packages_helper.rb
+++ b/app/helpers/work_packages_helper.rb
@@ -173,7 +173,7 @@ module WorkPackagesHelper
# Returns a string of css classes that apply to the issue
def work_package_css_classes(work_package)
- s = "work_package preview-trigger".html_safe
+ s = "work_package op-hover-card--preview-trigger".html_safe
s << " status-#{work_package.status.position}" if work_package.status
s << " priority-#{work_package.priority.position}" if work_package.priority
s << " closed" if work_package.closed?
diff --git a/app/views/work_packages/hover_card/show.html.erb b/app/views/work_packages/hover_card/show.html.erb
new file mode 100644
index 00000000000..8385b69e31d
--- /dev/null
+++ b/app/views/work_packages/hover_card/show.html.erb
@@ -0,0 +1,3 @@
+
+ Hallo WELT
+
diff --git a/config/initializers/permissions.rb b/config/initializers/permissions.rb
index d0ea7360287..a47ee9006cd 100644
--- a/config/initializers/permissions.rb
+++ b/config/initializers/permissions.rb
@@ -217,7 +217,8 @@ Rails.application.reloader.to_prepare do
work_packages: %i[show index],
work_packages_api: [:get],
"work_packages/reports": %i[report report_details],
- "work_packages/menus": %i[show]
+ "work_packages/menus": %i[show],
+ "work_packages/hover_card": %i[show]
},
permissible_on: %i[work_package project],
contract_actions: { work_packages: %i[read] }
diff --git a/config/routes.rb b/config/routes.rb
index 0eaec67895d..63631830755 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -571,6 +571,8 @@ Rails.application.routes.draw do
resources :work_packages, only: [:index] do
concerns :shareable
+ get "hover_card" => "work_packages/hover_card#show", on: :member
+
# move bulk of wps
get "move/new" => "work_packages/moves#new", on: :collection, as: "new_move"
post "move" => "work_packages/moves#create", on: :collection, as: "move"
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index f8d39a6f527..a35922776ad 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -53,7 +53,7 @@ import { OpenprojectDashboardsModule } from 'core-app/features/dashboards/openpr
import {
OpenprojectWorkPackageGraphsModule,
} from 'core-app/shared/components/work-package-graphs/openproject-work-package-graphs.module';
-import { PreviewTriggerService } from 'core-app/core/setup/globals/global-listeners/preview-trigger.service';
+import { HoverCardTriggerService } from 'core-app/core/setup/globals/global-listeners/hover-card-trigger.service';
import { OpenprojectOverviewModule } from 'core-app/features/overview/openproject-overview.module';
import { OpenprojectMyPageModule } from 'core-app/features/my-page/openproject-my-page.module';
import { OpenprojectProjectsModule } from 'core-app/features/projects/openproject-projects.module';
@@ -77,8 +77,8 @@ import {
PasswordConfirmationModalComponent,
} from 'core-app/shared/components/modals/request-for-confirmation/password-confirmation.modal';
import {
- WpPreviewModalComponent,
-} from 'core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal';
+ HoverCardComponent,
+} from 'core-app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal';
import {
OpHeaderProjectSelectComponent,
} from 'core-app/shared/components/header-project-select/header-project-select.component';
@@ -240,7 +240,7 @@ import { SpotSwitchComponent } from 'core-app/spot/components/switch/switch.comp
export function initializeServices(injector:Injector) {
return () => {
- const PreviewTrigger = injector.get(PreviewTriggerService);
+ const PreviewTrigger = injector.get(HoverCardTriggerService);
const topMenuService = injector.get(TopMenuService);
const keyboardShortcuts = injector.get(KeyboardShortcutService);
// Conditionally add the Revit Add-In settings button
@@ -370,7 +370,7 @@ export function initializeServices(injector:Injector) {
ConfirmDialogModalComponent,
DynamicContentModalComponent,
PasswordConfirmationModalComponent,
- WpPreviewModalComponent,
+ HoverCardComponent,
// Main menu
MainMenuResizerComponent,
diff --git a/frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts b/frontend/src/app/core/setup/globals/global-listeners/hover-card-trigger.service.ts
similarity index 67%
rename from frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts
rename to frontend/src/app/core/setup/globals/global-listeners/hover-card-trigger.service.ts
index 239f5e5dabe..8787b1304f8 100644
--- a/frontend/src/app/core/setup/globals/global-listeners/preview-trigger.service.ts
+++ b/frontend/src/app/core/setup/globals/global-listeners/hover-card-trigger.service.ts
@@ -28,10 +28,10 @@
import { Injectable, Injector, NgZone } from '@angular/core';
import { OpModalService } from 'core-app/shared/components/modal/modal.service';
-import { WpPreviewModalComponent } from 'core-app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal';
+import { HoverCardComponent } from 'core-app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal';
@Injectable({ providedIn: 'root' })
-export class PreviewTriggerService {
+export class HoverCardTriggerService {
private modalElement:HTMLElement;
private mouseInModal = false;
@@ -44,7 +44,7 @@ export class PreviewTriggerService {
}
setupListener() {
- jQuery(document.body).on('mouseover', '.preview-trigger', (e) => {
+ jQuery(document.body).on('mouseover', '.op-hover-card--preview-trigger', (e) => {
e.preventDefault();
e.stopPropagation();
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -58,8 +58,9 @@ export class PreviewTriggerService {
}
this.opModalService.show(
- WpPreviewModalComponent,
+ HoverCardComponent,
this.injector,
+ // TODO
{ workPackageLink: href, event: e },
true,
).subscribe((previewModal) => {
@@ -69,16 +70,16 @@ export class PreviewTriggerService {
}
});
- jQuery(document.body).on('mouseleave', '.preview-trigger', () => {
+ jQuery(document.body).on('mouseleave', '.op-hover-card--preview-trigger', () => {
this.closeAfterTimeout();
});
- jQuery(document.body).on('mouseleave', '.op-wp-preview-modal', () => {
+ jQuery(document.body).on('mouseleave', '.op-hover-card', () => {
this.mouseInModal = false;
this.closeAfterTimeout();
});
- jQuery(document.body).on('mouseenter', '.op-wp-preview-modal', () => {
+ jQuery(document.body).on('mouseenter', '.op-hover-card', () => {
this.mouseInModal = true;
});
}
@@ -92,21 +93,4 @@ export class PreviewTriggerService {
}, 100);
});
}
-
- private isMouseOverPreview(e:JQuery.MouseLeaveEvent) {
- if (!this.modalElement) {
- return false;
- }
-
- const previewElement = jQuery(this.modalElement.children[0]);
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- if (previewElement && previewElement.offset()) {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const horizontalHover = e.pageX >= Math.floor(previewElement.offset()!.left) && e.pageX < previewElement.offset()!.left + previewElement.width()!;
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const verticalHover = e.pageY >= Math.floor(previewElement.offset()!.top) && e.pageY < previewElement.offset()!.top + previewElement.height()!;
- return horizontalHover && verticalHover;
- }
- return false;
- }
}
diff --git a/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.html b/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.html
index 5715653d381..d652b451622 100644
--- a/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.html
+++ b/frontend/src/app/shared/components/fields/macros/work-package-quickinfo-macro.html
@@ -9,7 +9,7 @@
[displayFieldOptions]="{ writable: false }"
fieldName="type">
-
diff --git a/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.html b/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.html
new file mode 100644
index 00000000000..0143d9427fc
--- /dev/null
+++ b/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.sass b/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.sass
new file mode 100644
index 00000000000..ca901fb7c05
--- /dev/null
+++ b/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.sass
@@ -0,0 +1,10 @@
+@import "helpers"
+
+.op-hover-card
+ position: absolute
+ background-color: var(--body-background)
+ z-index: 5000
+ min-width: 350px
+ box-shadow: var(--shadow-floating-large)
+ pointer-events: all
+ padding: 1rem
diff --git a/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.ts b/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.ts
similarity index 59%
rename from frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.ts
rename to frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.ts
index a5ebb78b1c0..e29089da21b 100644
--- a/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.ts
+++ b/frontend/src/app/shared/components/modals/preview-modal/hover-card-modal/hover-card.modal.ts
@@ -32,17 +32,12 @@ import {
Component,
ElementRef,
Inject,
- OnInit,
Input,
+ OnInit,
} from '@angular/core';
import { OpModalComponent } from 'core-app/shared/components/modal/modal.component';
-import { OpModalLocalsToken, OpModalService } from 'core-app/shared/components/modal/modal.service';
+import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service';
import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types';
-import { I18nService } from 'core-app/core/i18n/i18n.service';
-import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
-import idFromLink from 'core-app/features/hal/helpers/id-from-link';
-import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service';
-import { StateService } from '@uirouter/core';
import {
computePosition,
flip,
@@ -50,57 +45,57 @@ import {
Placement,
shift,
} from '@floating-ui/dom';
+import { WorkPackageIsolatedQuerySpaceDirective } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive';
+import { fromEvent } from 'rxjs';
import {
- WorkPackageIsolatedQuerySpaceDirective,
-} from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive';
+ filter,
+ tap,
+ throttleTime,
+} from 'rxjs/operators';
@Component({
- templateUrl: './wp-preview.modal.html',
- styleUrls: ['./wp-preview.modal.sass'],
+ templateUrl: './hover-card.modal.html',
+ styleUrls: ['./hover-card.modal.sass'],
changeDetection: ChangeDetectionStrategy.OnPush,
hostDirectives: [WorkPackageIsolatedQuerySpaceDirective],
})
-export class WpPreviewModalComponent extends OpModalComponent implements OnInit {
- public workPackage:WorkPackageResource;
-
- public text = {
- created_by: this.i18n.t('js.label_created_by'),
- };
+export class HoverCardComponent extends OpModalComponent implements OnInit {
+ @Input() public turboFrameSrc:string = "/work_packages/50/hover_card";
@Input() public alignment?:Placement = 'bottom-end';
@Input() public allowRepositioning? = true;
+ public test:string;
+
constructor(
readonly elementRef:ElementRef,
@Inject(OpModalLocalsToken) readonly locals:OpModalLocalsMap,
readonly cdRef:ChangeDetectorRef,
- readonly i18n:I18nService,
- readonly apiV3Service:ApiV3Service,
- readonly opModalService:OpModalService,
- readonly $state:StateService,
) {
super(locals, cdRef, elementRef);
}
ngOnInit() {
super.ngOnInit();
- const { workPackageLink } = this.locals;
- const workPackageId = idFromLink(workPackageLink as string|null);
- this
- .apiV3Service
- .work_packages
- .id(workPackageId)
- .requireAndStream()
- .subscribe((workPackage:WorkPackageResource) => {
- this.workPackage = workPackage;
- this.cdRef.detectChanges();
+ this.test = this.turboFrameSrc;
- const modal = this.elementRef.nativeElement as HTMLElement;
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-explicit-any
- void this.reposition(modal, this.locals.event.target as HTMLElement);
- });
+ // TODO
+ fromEvent(document, 'turbo:frame-load')
+ .pipe(
+ filter((event:CustomEvent) => {
+ return (event.target as HTMLElement).id?.includes('op-hover-card-body');
+ }),
+ throttleTime(100),
+ tap(() => {
+ this.cdRef.detectChanges();
+
+ const modal = this.elementRef.nativeElement as HTMLElement;
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-explicit-any
+ void this.reposition(modal, this.locals.event.target as HTMLElement);
+ }),
+ );
}
public async reposition(element:HTMLElement, target:HTMLElement) {
@@ -125,9 +120,4 @@ export class WpPreviewModalComponent extends OpModalComponent implements OnInit
top: `${y}px`,
});
}
-
- public openStateLink(event:{ workPackageId:string; requestedState:string }) {
- const params = { workPackageId: event.workPackageId };
- void this.$state.go(event.requestedState, params);
- }
}
diff --git a/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.html b/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.html
deleted file mode 100644
index f87dd3384d3..00000000000
--- a/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.sass b/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.sass
deleted file mode 100644
index 2ceae2dfb05..00000000000
--- a/frontend/src/app/shared/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.sass
+++ /dev/null
@@ -1,9 +0,0 @@
-@import "helpers"
-
-.op-wp-preview-modal
- position: absolute
- z-index: 5000
- min-width: 350px
- padding: 0px
- box-shadow: 0px 0px 5px 2px rgba(0, 0, 0, 0.25)
- pointer-events: all
\ No newline at end of file
diff --git a/lib/open_project/text_formatting/filters/mention_filter.rb b/lib/open_project/text_formatting/filters/mention_filter.rb
index f007d464ea0..4b7ac6a2bd9 100644
--- a/lib/open_project/text_formatting/filters/mention_filter.rb
+++ b/lib/open_project/text_formatting/filters/mention_filter.rb
@@ -75,7 +75,7 @@ module OpenProject::TextFormatting
def work_package_mention(work_package)
link_to("##{work_package.id}",
work_package_path_or_url(id: work_package.id, only_path: context[:only_path]),
- class: "issue work_package preview-trigger")
+ class: "issue work_package op-hover-card--preview-trigger")
end
def class_from_mention(mention)
diff --git a/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb b/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb
index dfffcf173db..4b2fd7bff2e 100644
--- a/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb
+++ b/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb
@@ -66,7 +66,7 @@ module OpenProject::TextFormatting::Matchers
def render_work_package_link(wp_id)
link_to("##{wp_id}",
work_package_path_or_url(id: wp_id, only_path: context[:only_path]),
- class: "issue work_package preview-trigger")
+ class: "issue work_package op-hover-card--preview-trigger")
end
end
end
diff --git a/spec/features/work_packages/details/markdown/activity_comments_spec.rb b/spec/features/work_packages/details/markdown/activity_comments_spec.rb
index 06ba11d9d6e..3ceed0353dd 100644
--- a/spec/features/work_packages/details/markdown/activity_comments_spec.rb
+++ b/spec/features/work_packages/details/markdown/activity_comments_spec.rb
@@ -247,7 +247,7 @@ RSpec.describe "activity comments", :js do
wp_page.expect_comment text: "Single ##{work_package2.id}"
expect(page).to have_css(".user-comment opce-macro-wp-quickinfo", count: 2)
- expect(page).to have_css(".user-comment .work-package--quickinfo.preview-trigger", count: 2)
+ expect(page).to have_css(".user-comment .op-hover-card--preview-trigger", count: 2)
end
end
diff --git a/spec/features/wysiwyg/macros/quicklink_macros_spec.rb b/spec/features/wysiwyg/macros/quicklink_macros_spec.rb
index d487d2ad5e1..40ebe4d116f 100644
--- a/spec/features/wysiwyg/macros/quicklink_macros_spec.rb
+++ b/spec/features/wysiwyg/macros/quicklink_macros_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe "Wysiwyg work package quicklink macros", :js do
# Expect output widget
within("#content") do
expect(page).to have_link("##{work_package.id}")
- expect(page).to have_no_css(".work-package--quickinfo.preview-trigger")
+ expect(page).to have_no_css(".op-hover-card--preview-trigger")
end
# Edit page again
@@ -77,7 +77,7 @@ RSpec.describe "Wysiwyg work package quicklink macros", :js do
expected_macro_text = "#{work_package.type.name.upcase} ##{work_package.id}: My subject"
expect(page).to have_css("opce-macro-wp-quickinfo", text: expected_macro_text)
expect(page).to have_css("span", text: work_package.type.name.upcase)
- expect(page).to have_css(".work-package--quickinfo.preview-trigger", text: "##{work_package.id}")
+ expect(page).to have_css(".op-hover-card--preview-trigger", text: "##{work_package.id}")
expect(page).to have_css("span", text: "My subject")
end
@@ -102,7 +102,7 @@ RSpec.describe "Wysiwyg work package quicklink macros", :js do
expect(page).to have_css("opce-macro-wp-quickinfo", text: expected_macro_text)
expect(page).to have_css("span", text: work_package.status.name)
expect(page).to have_css("span", text: work_package.type.name.upcase)
- expect(page).to have_css(".work-package--quickinfo.preview-trigger", text: "##{work_package.id}")
+ expect(page).to have_css(".op-hover-card--preview-trigger", text: "##{work_package.id}")
expect(page).to have_css("span", text: "My subject")
# Dates are being rendered in two nested spans
expect(page).to have_css("span", text: "01/01/2020", count: 2)
diff --git a/spec/lib/api/v3/repositories/revision_representer_spec.rb b/spec/lib/api/v3/repositories/revision_representer_spec.rb
index 13ce2f9b2b7..24e90c30c07 100644
--- a/spec/lib/api/v3/repositories/revision_representer_spec.rb
+++ b/spec/lib/api/v3/repositories/revision_representer_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe API::V3::Repositories::RevisionRepresenter do
id = work_package.id
str = "Totally references "
str << "##{id}"
end
diff --git a/spec/lib/open_project/text_formatting/markdown/in_tool_links_spec.rb b/spec/lib/open_project/text_formatting/markdown/in_tool_links_spec.rb
index 22968a58013..94cb8580fb8 100644
--- a/spec/lib/open_project/text_formatting/markdown/in_tool_links_spec.rb
+++ b/spec/lib/open_project/text_formatting/markdown/in_tool_links_spec.rb
@@ -267,7 +267,7 @@ RSpec.describe OpenProject::TextFormatting,
let(:work_package_link) do
link_to("##{work_package.id}",
work_package_path(work_package),
- class: "issue work_package preview-trigger op-uc-link",
+ class: "issue work_package op-hover-card--preview-trigger op-uc-link",
target: "_top")
end
@@ -337,7 +337,7 @@ RSpec.describe OpenProject::TextFormatting,
let(:work_package_link) do
link_to("##{work_package.id}",
work_package_path(work_package),
- class: "issue work_package preview-trigger op-uc-link",
+ class: "issue work_package op-hover-card--preview-trigger op-uc-link",
target: "_top")
end
@@ -656,7 +656,7 @@ RSpec.describe OpenProject::TextFormatting,
let(:expected) do
<<~EXPECTED
CookBook documentation
- ##{work_package.id}
+ ##{work_package.id}
[[CookBook documentation]]
diff --git a/spec/requests/api/v3/render_resource_spec.rb b/spec/requests/api/v3/render_resource_spec.rb
index c66def7ce8e..bf8a8e6f38f 100644
--- a/spec/requests/api/v3/render_resource_spec.rb
+++ b/spec/requests/api/v3/render_resource_spec.rb
@@ -90,7 +90,7 @@ RSpec.describe "API v3 Render resource" do
<<~HTML
Hello World! Have a look at
- ##{id}
@@ -180,7 +180,7 @@ RSpec.describe "API v3 Render resource" do
it_behaves_like "valid response" do
let(:text) do
- "Hello *World*! Have a look at #1
\n\nwith two lines.
"
+ "Hello *World*! Have a look at #1
\n\nwith two lines.
"
end
end
end