diff --git a/web/e2e/smoke.spec.js b/web/e2e/smoke.spec.js
index ebe855d..3fa8591 100644
--- a/web/e2e/smoke.spec.js
+++ b/web/e2e/smoke.spec.js
@@ -2440,7 +2440,8 @@ test('en note kan deles med et projekts folk, og tages tilbage igen', async ({ p
await page.keyboard.type('Fælles aftale om leveringen');
await page.getByRole('textbox', { name: 'Notens tekst' }).blur();
- // Shared by being filed in a project. Not an access list of its own.
+ // Filing it in a project — the controls live in the Del popover in the footer.
+ await page.locator('.sharewrap > button').click();
await page.getByLabel('Læg i projekt').selectOption({ label: 'Delenoter' });
await expect(page.getByText('delt med projektets folk')).toBeVisible();
@@ -2451,6 +2452,7 @@ test('en note kan deles med et projekts folk, og tages tilbage igen', async ({ p
// Taken back out, it is the author's alone again.
await sidebar.getByRole('link', { name: 'Noter', exact: true }).click();
await page.getByRole('button', { name: /Fælles aftale/ }).click();
+ await page.locator('.sharewrap > button').click();
await page.getByLabel('Læg i projekt').selectOption({ label: 'Intet projekt' });
await expect(page.getByText('din igen')).toBeVisible();
@@ -2487,10 +2489,12 @@ test('en note kan deles med en person direkte, og dukker op hos dem', async ({ b
await page.keyboard.type('Ferieplan for hele holdet');
await page.getByRole('textbox', { name: 'Notens tekst' }).blur();
- const share = page.locator('.people-share');
+ // Sharing lives in a popover behind the Del button in the footer.
+ await page.locator('.sharewrap > button').click();
+ const share = page.locator('.sharepanel');
await expect(share).toBeVisible();
await share.locator('.addshare select').first().selectOption({ label: 'Sofie' });
- await page.getByRole('button', { name: 'Del', exact: true }).click();
+ await share.getByRole('button', { name: 'Tilføj' }).click();
// She is on the note now, with the role she was given.
const row = share.locator('.sharelist li').filter({ hasText: 'Sofie' });
@@ -2981,7 +2985,8 @@ test('en note kan oprettes inde fra et projekt og hører til det med det samme',
await page.waitForTimeout(1200);
// Den hører til projektet med det samme: "Læg i projekt" står på projektet,
- // uden at nogen har valgt det bagefter.
+ // uden at nogen har valgt det bagefter. Kontrollen ligger i Del-popoveren.
+ await page.locator('.sharewrap > button').click();
await expect(page.getByLabel('Læg i projekt')).toHaveValue(/.+/);
const shared = await page.getByLabel('Læg i projekt').evaluate((el) => el.selectedOptions[0].textContent);
expect(shared).toContain('Tagprojekt');
diff --git a/web/src/lib/locales/da.js b/web/src/lib/locales/da.js
index f1028ac..f24da7f 100644
--- a/web/src/lib/locales/da.js
+++ b/web/src/lib/locales/da.js
@@ -25,7 +25,8 @@ export const da = {
'notes.sharedBy': 'Delt af {name}',
'notes.sharePeople': 'Del med personer',
'notes.sharePick': 'Vælg en person …',
- 'notes.shareAdd': 'Del',
+ 'notes.shareButton': 'Del',
+ 'notes.shareAdd': 'Tilføj',
'notes.shareRoleViewer': 'Kan læse',
'notes.shareRoleEditor': 'Kan rette',
'notes.shareRemove': 'Fjern adgang',
diff --git a/web/src/lib/locales/en.js b/web/src/lib/locales/en.js
index d4bc226..87b932a 100644
--- a/web/src/lib/locales/en.js
+++ b/web/src/lib/locales/en.js
@@ -21,7 +21,8 @@ export const en = {
'notes.sharedBy': 'Shared by {name}',
'notes.sharePeople': 'Share with people',
'notes.sharePick': 'Choose someone …',
- 'notes.shareAdd': 'Share',
+ 'notes.shareButton': 'Share',
+ 'notes.shareAdd': 'Add',
'notes.shareRoleViewer': 'Can view',
'notes.shareRoleEditor': 'Can edit',
'notes.shareRemove': 'Remove access',
diff --git a/web/src/routes/noter/+page.svelte b/web/src/routes/noter/+page.svelte
index 6e24c6f..dade9b4 100644
--- a/web/src/routes/noter/+page.svelte
+++ b/web/src/routes/noter/+page.svelte
@@ -193,6 +193,26 @@
let shareBusy = $state(false);
const ownsSelected = $derived(!!selected && selected.created_by === app.user?.id);
+ // The whole of sharing — a project and named people — lives in a popover behind
+ // one button, so the note's footer stays a status and three buttons rather than
+ // a row that runs off its own width.
+ let shareOpen = $state(false);
+ let shareWrap = $state(null);
+ // Closing on a switch of note, and only then: the panel belongs to the note it
+ // was opened on. Tracked by id rather than by the object, so a save that returns
+ // a fresh copy of the same note does not slam the panel shut mid-use.
+ let sharePanelFor = $state(null);
+ $effect(() => {
+ const id = selected?.id ?? null;
+ if (id !== sharePanelFor) {
+ shareOpen = false;
+ sharePanelFor = id;
+ }
+ });
+ function onShareOutside(event) {
+ if (shareOpen && shareWrap && !shareWrap.contains(event.target)) shareOpen = false;
+ }
+
$effect(() => {
const id = selected?.id;
// Nulstil ved hvert skift, så den forrige notes folk ikke står et øjeblik
@@ -638,6 +658,13 @@
{t('notes.title')} · verdande
+ {
+ if (e.key === 'Escape' && shareOpen) shareOpen = false;
+ }}
+/>
+