Skip to content

Updates Image Editor so that cropper move and resize are keyboard-accessible #17073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 22 commits into
base: 5.8
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
66092d2
Make cropper container keyboard focusable
gcamacho079 Mar 26, 2025
d4f0b04
Move toggle function, add keydown listener for cropper
gcamacho079 Mar 26, 2025
d5c39e3
Commit
gcamacho079 Mar 31, 2025
f0a3994
Merge branch '5.7' into feature/pt-475-crop-frame-cannot-be-moved-usi…
gcamacho079 Apr 11, 2025
98aab89
Remove default focus outline from cropping canvas, make cropping rect…
gcamacho079 Apr 11, 2025
ad12b4d
Exit keydown handler if not a directional key
gcamacho079 Apr 11, 2025
1e74d0f
Update cropper rectangle placement based on keyboard input
gcamacho079 Apr 14, 2025
9aca6ee
Remove unused function
gcamacho079 Apr 14, 2025
b34ca22
Reuse code for both drag and keyboard move
gcamacho079 Apr 14, 2025
24c329e
Start moving to button versus having the canvas be focuable
gcamacho079 Apr 15, 2025
fd7c25a
Toggle button state on activate, toggle picked up mode
gcamacho079 Apr 15, 2025
79c68fe
Add temp announcer to troubleshoot sr messages, when we add those in
gcamacho079 Apr 16, 2025
cfc1a5c
Find position of cropper center
gcamacho079 Apr 16, 2025
faad842
Fix bug where arrows were still activating tab change, move cropping …
gcamacho079 Apr 16, 2025
d2de5c7
Add button macro and use for all corner resize and crop moving buttons
gcamacho079 Apr 17, 2025
7595227
Pass corner info to data attribute on resize handle button
gcamacho079 Apr 21, 2025
420219b
Merge 5.8 and build
gcamacho079 Apr 21, 2025
556d5d2
Start refactoring for exclusive button functionality
gcamacho079 Apr 21, 2025
44d7d5b
Track which corner is picked up and change handle color (WIP)
gcamacho079 Apr 24, 2025
663cf3a
Add circle around selected corner based on which one has been picked up
gcamacho079 Apr 24, 2025
c3dc123
Make corner focus ring more apparent and add new fxn for getting corn…
gcamacho079 Apr 24, 2025
660cd22
Refactor position message code
gcamacho079 Apr 24, 2025
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
80 changes: 80 additions & 0 deletions src/templates/_special/image_editor.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% import "_includes/forms" as forms %}
{% set orientation = craft.app.locale.getOrientation() -%}
{% do view.registerTranslations('app', [
'Original',
'Square',
Expand All @@ -8,6 +9,22 @@
class: 'visually-hidden',
text: 'Edit Image'|t('app'),
}) }}

{% macro iconButton(config) %}
{% set id = config.id ?? null %}
{% tag 'button' with {
id: id,
class: ['btn', 'flex', 'flex-nowrap', 'gap-s'],
type: 'button',
aria: {
pressed: 'false',
},
}|merge(config.buttonAttributes ?? {}, recursive=true) %}
<div class="cp-icon puny">{{ iconSvg(config.icon) }}</div>
<div>{{ config.label }}</div>
{% endtag %}
{% endmacro %}

<div class="tabs">
<ul role="tablist">
<li id="rotate-tab" data-view="rotate" role="tab" tabindex="0" aria-selected="true"><i></i>{{ 'Rotate'|t('app') }}</li>
Expand Down Expand Up @@ -60,9 +77,72 @@
value: 'none',
options: constraintOptions,
}) }}

{% set cropHandleBtns = [
{
label: orientation == 'ltr' ? 'Top Left Handle'|t('app') : 'Top Right Handle'|t('app'),
corner: 'top-left',
},
{
label: orientation == 'ltr' ? 'Top Right Handle'|t('app') : 'Top Left Handle'|t('app'),
corner: 'top-right',
},
{
label: orientation == 'ltr' ? 'Bottom Left Handle'|t('app') : 'Bottom Right Handle'|t('app'),
corner: 'bottom-left',
},
{
label: orientation == 'ltr' ? 'Bottom Right Handle'|t('app') : 'Bottom Left Handle'|t('app'),
corner: 'bottom-right',
},
] %}
<fieldset class="cropper-edit">
<legend>
{{ 'Edit {type}'|t('app', {
type: 'Cropping Rectangle'|t('app'),
}) }}
</legend>
<div class="cropper-edit__btns">
{{ _self.iconButton({
icon: 'up-down-left-right',
label: 'Cropping Rectangle'|t('app'),
id: 'cropper-handle',
buttonAttributes: {
data: {
'crop-editor': 'rectangle',
},
class: ['cropper-edit__btn']
}
}) }}
{% for button in cropHandleBtns %}
{{ _self.iconButton({
icon: 'expand',
label: button.label,
buttonAttributes: {
data: {
'crop-editor': 'corner',
'corner-handle': button.corner,
},
class: ['cropper-edit__btn']
}
}) }}
{% endfor %}
</div>
</fieldset>
</div>
</div>


<div class="image-container">
<div id="cropper-actions" class="hidden">
{% for button in cropHandleBtns %}
{{ tag ('button', {
type: 'button',
text: button.label,
class: 'visually-hidden',
}) }}
{% endfor %}
</div>
<div class="image">
<canvas id="image-canvas">
</canvas>
Expand Down
3 changes: 3 additions & 0 deletions src/translations/en/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@
'Credentialed' => 'Credentialed',
'Critical' => 'Critical',
'Crop' => 'Crop',
'Cropping Rectangle' => 'Cropping Rectangle',
'Currency' => 'Currency',
'Current Password' => 'Current Password',
'Current User Condition' => 'Current User Condition',
Expand Down Expand Up @@ -1907,6 +1908,7 @@
'Use defaults' => 'Use defaults',
'Use for element thumbnails' => 'Use for element thumbnails',
'Use shapes to represent statuses' => 'Use shapes to represent statuses',
'Use the arrow keys to change position, Spacebar to drop, Escape key to cancel.' => 'Use the arrow keys to change position, Spacebar to drop, Escape key to cancel.',
'Use the loaded project config' => 'Use the loaded project config',
'Use this field’s values as search keywords' => 'Use this field’s values as search keywords',
'Used by' => 'Used by',
Expand Down Expand Up @@ -2213,6 +2215,7 @@
'{filename} isn’t selectable for this field.' => '{filename} isn’t selectable for this field.',
'{first, number}-{last, number} of {total, number} {total, plural, =1{{item}} other{{items}}}' => '{first, number}–{last, number} of {total, number} {total, plural, =1{{item}} other{{items}}}',
'{first}-{last} of {total}' => '{first}–{last} of {total}',
'{item} picked up.' => '{item} picked up.',
'{max, plural, =1{Author} other {Authors}}' => '{max, plural, =1{Author} other {Authors}}',
'{names} {total, plural, =1{is installed as a trial} other{are installed as trials}}.' => '{names} {total, plural, =1{is installed as a trial} other{are installed as trials}}.',
'{name} Setup' => '{name} Setup',
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/css/cp.css.map

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions src/web/assets/cp/src/css/_image_editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -530,3 +530,25 @@ html.noscroll body {
}
}
}

#cropping-canvas {
--focus-ring: none;
}

#cropper-actions {
position: absolute;
z-index: 1;
inset-block-start: 0;
inset-inline-start: 0;
}

.cropper-edit__btns {
max-width: 400px;
display: flex;
flex-wrap: wrap;
align-items: flex-start;

.btn {
flex: 0 1 50%;
}
}
4 changes: 4 additions & 0 deletions src/web/assets/cp/src/css/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3670,6 +3670,10 @@ table {
@include mixins.svg-mask(var(--icon-color, var(--ui-control-color)));
}

&.xl {
--icon-size: calc(24rem / 16);
}

&.large {
--icon-size: calc(20rem / 16);
}
Expand Down
Loading