Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ See COPYRIGHT and LICENSE files for more details.
<%= grid_layout(
"op-work-package-card",
tag: :article,
classes: { "op-work-package-card_with-metric": metric? }
**card_arguments
) do |grid| %>
<% grid.with_area(:info_line) do %>
<%# TODO(73089): allow callers to pass arguments through to InfoLineComponent (e.g. status presentation, variants). %>
Expand All @@ -43,7 +43,7 @@ See COPYRIGHT and LICENSE files for more details.
<% end %>
<% end %>

<% grid.with_area(:menu) do %>
<% grid.with_area(:menu, classes: 'hidden-for-drag-preview') do %>
<% if menu? %>
<%= menu %>
<% else %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,26 @@ class WorkPackageCardComponent < ApplicationComponent
# @param work_package [WorkPackage] the work package this card represents.
# @param menu_src [String, NilClass] optional lazy menu source. Prefer the
# `with_menu(src:)` slot for new call sites.
def initialize(work_package:, menu_src: nil)
# @param system_arguments [Hash] forwarded to the root card element.
def initialize(work_package:, menu_src: nil, **system_arguments)
super()

@work_package = work_package
@menu_src = menu_src
@system_arguments = system_arguments
@system_arguments[:classes] = class_names(
@system_arguments[:classes],
"Box-card"
)
end

def card_arguments
@system_arguments.deep_dup.tap do |arguments|
arguments[:classes] = class_names(
arguments[:classes],
"op-work-package-card_with-metric": metric?
)
end
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions config/locales/js-en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ en:
Drag on editor field to inline image or reference attachment. Closed editor fields will be opened while you keep dragging.
quarantined_hint: "The file is quarantined, as a virus was found. It is not available for download."

sortable_lists:
drag_handle:
instructions: "Drag to reposition this item."

autocomplete_ng_select:
add_tag: "Add item"
clear_all: "Clear all"
Expand Down
64 changes: 64 additions & 0 deletions frontend/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Frontend

The OpenProject frontend context describes user interface interaction language shared across frontend features.

## Language

**Drag preview**:
The temporary visual representation that follows the pointer during a drag.
_Avoid_: Mirror

**Drag source**:
The original draggable element while it is being dragged.
_Avoid_: Placeholder

**Drop indicator**:
A visual marker that shows where a dragged object will be placed.
_Avoid_: Placeholder

**Drop placeholder**:
A reserved space that approximates the size of the dragged object at a candidate drop location.
_Avoid_: Drop indicator

**Drop target**:
An area that can accept or reject a dragged object.
_Avoid_: Container

**Empty drop zone**:
An empty area that can accept a dragged object when there is no existing object to anchor a drop indicator.
_Avoid_: Placeholder

**Sortable item**:
An object that can be repositioned by drag and drop within or between sortable lists.
_Avoid_: Draggable item

**Sortable item type**:
A category used to decide whether a sortable item may be dropped into a sortable list.
_Avoid_: Draggable type

**Sortable list**:
An ordered list whose sortable items can be repositioned by drag and drop.
_Avoid_: Container

## Relationships

- A **Drag source** remains at the original location while the **Drag preview** follows the pointer.
- A **Drop indicator** marks a candidate location without reserving full object-sized space.
- A **Drop placeholder** reserves object-sized space at a candidate location.
- An **Empty drop zone** may combine the affordances of a **Drop target**, **Drop indicator**, and **Drop placeholder**.
- A **Sortable item** has a **Sortable item type**.
- A **Sortable list** may accept only specific **Sortable item types**.

## Example Dialogue

> **Dev:** "What should we call the floating visual during a drag?"
> **Domain expert:** "The **Drag preview**."
> **Dev:** "Is an 8px highlighted gap a placeholder?"
> **Domain expert:** "No — it is a **Drop indicator** because it marks a candidate location without reserving full object-sized space."

## Flagged Ambiguities

- "mirror" is Dragula-specific — resolved: use **Drag preview**.
- "placeholder" was used for highlighted gaps — resolved: use **Drop indicator** unless full object-sized space is reserved.
- "container" was used for drop areas — resolved: use **Drop target** for drag-and-drop interaction language.
- "draggable type" was inherited from the generic drag-and-drop controller — resolved: use **Sortable item type**.
64 changes: 64 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@
"@appsignal/javascript": "^1.6.1",
"@appsignal/plugin-breadcrumbs-console": "^1.1.37",
"@appsignal/plugin-breadcrumbs-network": "^1.1.24",
"@atlaskit/pragmatic-drag-and-drop": "^1.8.1",
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
"@blocknote/core": "^0.44.2",
"@blocknote/mantine": "^0.44.2",
"@blocknote/react": "^0.44.2",
Expand Down
80 changes: 57 additions & 23 deletions frontend/src/global_styles/primer/_overrides.sass
Original file line number Diff line number Diff line change
Expand Up @@ -156,42 +156,76 @@ ul.SegmentedControl,
.ActionListItem-label[class^="__hl_"], .ActionListItem-label[class*=" __hl_"]
color: var(--control-fgColor-disabled) !important

// hide hover when dragging with legacy Dragula implementation
.Box-row--hover-gray,
.Box-row--hover-blue
body[data-dragging="active"] &
background-color: unset
// .Box-row--focus-gray
// &:focus-visible
// background-color: var(--bgColor-muted)

.Box-row--focus-gray
&:focus-visible
background-color: var(--bgColor-muted)

.Box-row--focus-blue
&:focus-visible
background-color: var(--bgColor-accent-muted)
// .Box-row--focus-blue
// &:focus-visible
// background-color: var(--bgColor-accent-muted)
Comment on lines +159 to +165

.Box-row--clickable
cursor: pointer

.Box-row--draggable:not(.Box-row--clickable)
.Box-row--draggable
cursor: grab

// `:is(.Box-row--clickable)` bumps selector specificity so these rules win
// over Dragula's `.gu-*` transit styles; without it the mirror and source
// rows render transparent mid-drag. Revisit once #74172 / #73729 replace the
// legacy Dragula implementation.
.Box-row:is(.Box-row--clickable)
&[data-dragging="mirror"]
&:active
cursor: grabbing

.Box-row:is(.Box-row--draggable)
--op-backlogs-drop-gap-border-width: 2px
--op-backlogs-drop-gap-height: 8px

padding: 0

&[data-dragging="source"]
opacity: 0.4
background-color: var(--bgColor-inset)
box-shadow: inset 0 0 0 var(--borderWidth-default) var(--borderColor-muted)

&[data-drop-position]
position: relative

&::before,
&::after
position: absolute
left: 0
right: 0
height: var(--op-backlogs-drop-gap-height)
background: linear-gradient(
to bottom,
var(--bgColor-accent-muted) 0,
var(--bgColor-accent-muted) calc(50% - var(--op-backlogs-drop-gap-border-width) / 2),
var(--fgColor-accent) calc(50% - var(--op-backlogs-drop-gap-border-width) / 2),
var(--fgColor-accent) calc(50% + var(--op-backlogs-drop-gap-border-width) / 2),
var(--bgColor-accent-muted) calc(50% + var(--op-backlogs-drop-gap-border-width) / 2),
var(--bgColor-accent-muted) 100%
)
pointer-events: none
z-index: 1

&[data-drop-position="top"]::before
content: ''
top: calc(var(--op-backlogs-drop-gap-height) / -2)

&[data-drop-position="bottom"]::after
content: ''
bottom: calc(var(--op-backlogs-drop-gap-height) / -2)

.Box-card
// padding: var(--stack-padding-normal)
padding: var(--stack-padding-condensed) var(--stack-padding-normal)

&[data-preview]
background-color: var(--bgColor-default)
border: var(--borderWidth-default) solid var(--borderColor-default)
border-radius: var(--borderRadius-medium)
box-shadow: var(--shadow-floating-medium)
opacity: 1

&[data-dragging="source"]
opacity: 0.4
background-color: var(--bgColor-subtle)
box-shadow: inset 0 0 0 var(--borderWidth-default) var(--borderColor-muted)
.hidden-for-drag-preview
visibility: hidden

// Apply the mobile styles as soon as the banner itself is small
// Styles are copied from the PVC repo
Expand Down
Loading
Loading