diff --git a/resources/css/components/fieldtypes/bard.css b/resources/css/components/fieldtypes/bard.css
index 89cdf7e52f5..bf8d95463f6 100644
--- a/resources/css/components/fieldtypes/bard.css
+++ b/resources/css/components/fieldtypes/bard.css
@@ -38,6 +38,38 @@
}
}
}
+/* BARD / SELECTION STATE
+=================================================== */
+@layer ui {
+ /* Hide the selection outline when we have a modal or combobox open, since the focus outline will no longer be correct. */
+ body:not(:has([data-ui-modal-content], [data-ui-combobox-content])) {
+ /*
+ Here we're carefully controlling the “selection outline” when a set is selected with the mouse or keyboard.
+ Style the selection outline...
+ */
+ .st-set-is-selected:not(:has(
+ /* Except if... */
+ /* Any element inside the Set has focus (e.g. when editing inside a Bard field). This prevents the outer selection outline from showing while the user is actively working inside the Set. */
+ :focus-within,
+ /* When the options dropdown is open on an _inner_ set, the outer selection outline should be hidden. Instead we'll show a focus outline just for the replicator set that's selected. */
+ [data-ui-dropdown-trigger][data-state="open"],
+ /* When the dropdown has just closed (open → closed), keep the outline hidden briefly so it doesn't flicker back to the parent while focus settles. */
+ .st-dropdown-just-closed
+ )),
+ /* Show a focus outline _just_ on the outer set, when we have the dropdown open on the outer set */
+ .st-set-is-selected:has(> header [data-ui-dropdown-trigger][data-state="open"]),
+ /* Show a focus outline just for the replicator set that's selected. We're using a direct descendant selector here in case there are further nested replicator sets. */
+ [data-replicator-set]:has(> header [data-ui-dropdown-trigger][data-state="open"]) {
+ &::before {
+ content: '';
+ position: absolute;
+ inset: -1px;
+ pointer-events: none;
+ @apply border-2 border-blue-400 dark:border-blue-400 rounded-lg;
+ }
+ }
+ }
+}
/* BARD / FOOTER TOOLBAR
=================================================== */
@layer ui {
diff --git a/resources/css/elements/base.css b/resources/css/elements/base.css
index 465bf07665f..b6d33381399 100644
--- a/resources/css/elements/base.css
+++ b/resources/css/elements/base.css
@@ -92,7 +92,7 @@
- s*/
+ */
.show-focus-within:has(.show-focus-within_target:focus-visible) {
/* Stop the transition ruining the focus outline quickly appearing. */
transition: none;
diff --git a/resources/js/components/field-actions/FieldActions.vue b/resources/js/components/field-actions/FieldActions.vue
index 53d7bb35085..d70afa01b1a 100644
--- a/resources/js/components/field-actions/FieldActions.vue
+++ b/resources/js/components/field-actions/FieldActions.vue
@@ -16,17 +16,19 @@
+
@@ -59,5 +61,21 @@ export default {
return this.actions.filter((a) => !a.quick).length > 0;
},
},
+
+ methods: {
+ runQuickAction(action, event) {
+ const target = event?.currentTarget;
+
+ if (action.disabled) {
+ if (target instanceof HTMLButtonElement) {
+ // Re-focus after click so selection/focus styling doesn't jump to parent containers.
+ requestAnimationFrame(() => target.focus({ preventScroll: true }));
+ }
+ return;
+ }
+
+ action.run();
+ },
+ },
};
diff --git a/resources/js/components/fieldtypes/bard/Set.vue b/resources/js/components/fieldtypes/bard/Set.vue
index 50c14230375..5afce6aefac 100644
--- a/resources/js/components/fieldtypes/bard/Set.vue
+++ b/resources/js/components/fieldtypes/bard/Set.vue
@@ -4,11 +4,9 @@
ref="container"
class="shadow-ui-sm relative w-full rounded-lg border border-gray-300 bg-white text-base dark:border-white/10 dark:bg-gray-900 dark:inset-shadow-2xs dark:inset-shadow-black"
:class="{
- // We’re styling a Set so that it shows a “selection outline” when selected with the mouse or keyboard.
- // The extra `&:not(:has(:focus-within))` rule turns that outline off if any element inside the Set has focus (e.g. when editing inside a Bard field).
- // This prevents the outer selection outline from showing while the user is actively working inside the Set.
- 'st-set-is-selected [&:not(:has(:focus-within))]:border-blue-400! [&:not(:has(:focus-within))]:dark:border-blue-400! [&:not(:has(:focus-within))]:before:content-[\'\'] [&:not(:has(:focus-within))]:before:absolute [&:not(:has(:focus-within))]:before:inset-[-1px] [&:not(:has(:focus-within))]:before:pointer-events-none [&:not(:has(:focus-within))]:before:border-2 [&:not(:has(:focus-within))]:before:border-blue-400 [&:not(:has(:focus-within))]:dark:before:border-blue-400 [&:not(:has(:focus-within))]:before:rounded-lg': showSelectionHighlight,
+ 'st-set-is-selected': showSelectionHighlight,
'border-red-500': hasError,
+ 'st-dropdown-just-closed': dropdownJustClosed,
}"
:data-type="config.handle"
contenteditable="false"
@@ -49,7 +47,7 @@