From 1193c715790ff1608a3d397bce28bbb9fa4dd3bd Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Tue, 11 Nov 2025 15:59:17 +0100 Subject: [PATCH] tag picker --- frontend/src/desktop/createDesktopSurface.js | 3 +- .../src/documents/SelectionAssignmentMenu.jsx | 62 +++++++++---------- .../documents/panel/createDocumentsSurface.js | 3 +- frontend/src/styles/detail/detail-panels.css | 19 +++++- frontend/src/styles/sidebar/sidebar.css | 1 + 5 files changed, 53 insertions(+), 35 deletions(-) diff --git a/frontend/src/desktop/createDesktopSurface.js b/frontend/src/desktop/createDesktopSurface.js index 0ca4a33..2b1dd9a 100644 --- a/frontend/src/desktop/createDesktopSurface.js +++ b/frontend/src/desktop/createDesktopSurface.js @@ -84,12 +84,13 @@ const createDesktopSurface = ({ const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const detail = detailOpen && detailProps ? (() => { - const { onClose, onOpenPreview, ...restDetailProps } = detailProps; + const { onClose, onOpenPreview, tags: tagOptions, ...restDetailProps } = detailProps; return ( ); diff --git a/frontend/src/documents/SelectionAssignmentMenu.jsx b/frontend/src/documents/SelectionAssignmentMenu.jsx index e5ff17b..6bd0aa6 100644 --- a/frontend/src/documents/SelectionAssignmentMenu.jsx +++ b/frontend/src/documents/SelectionAssignmentMenu.jsx @@ -1,10 +1,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import useFloatingMenu from '../ui/useFloatingMenu'; -import { - PlusIcon, - CheckIcon, - CircleDashedCheckIcon, -} from '../ui/icons'; +import { CheckIcon, CircleDashedCheckIcon, PlusIcon } from '../ui/icons'; const STATE_ORDER = { all: 0, @@ -117,7 +113,8 @@ const SelectionAssignmentMenu = ({ ); const handleCreate = useCallback( - async () => { + async (event) => { + event?.preventDefault?.(); if (typeof onCreate !== 'function') { return; } @@ -144,9 +141,12 @@ const SelectionAssignmentMenu = ({ ); const canCreate = Boolean(onCreate); - const showCreateOption = canCreate - && query.trim().length > 0 - && !existingLabels.has(query.trim().toLowerCase()); + const trimmedQuery = query.trim(); + const queryKey = trimmedQuery.toLowerCase(); + const canSubmitCreate = canCreate + && trimmedQuery.length > 0 + && !existingLabels.has(queryKey) + && !pending; const handleTriggerClick = useCallback(() => { if (disabled) { @@ -184,15 +184,28 @@ const SelectionAssignmentMenu = ({ data-floating-position >
- setQuery(event.target.value)} - placeholder={placeholder} - aria-label={placeholder} - disabled={pending} - /> +
+ setQuery(event.target.value)} + placeholder={placeholder} + aria-label={placeholder} + disabled={pending} + /> + {canCreate ? ( + + ) : null} +
{filteredItems.length ? ( @@ -237,19 +250,6 @@ const SelectionAssignmentMenu = ({
{emptyMessage}
)}
- {showCreateOption ? ( - - ) : null} ) : null} diff --git a/frontend/src/documents/panel/createDocumentsSurface.js b/frontend/src/documents/panel/createDocumentsSurface.js index 794e2f4..a0ee776 100644 --- a/frontend/src/documents/panel/createDocumentsSurface.js +++ b/frontend/src/documents/panel/createDocumentsSurface.js @@ -93,12 +93,13 @@ const createDocumentsSurface = ({ const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const detail = detailOpen && detailProps ? (() => { - const { onClose, onOpenPreview, ...restDetailProps } = detailProps; + const { onClose, onOpenPreview, tags: tagOptions, ...restDetailProps } = detailProps; return ( ); diff --git a/frontend/src/styles/detail/detail-panels.css b/frontend/src/styles/detail/detail-panels.css index a51f469..f5584c6 100644 --- a/frontend/src/styles/detail/detail-panels.css +++ b/frontend/src/styles/detail/detail-panels.css @@ -191,7 +191,14 @@ border-bottom: 1px solid var(--border-subtle); } -.selection-assignment__header input { +.selection-assignment__form { + display: flex; + gap: 0.4rem; + align-items: stretch; +} + +.selection-assignment__form input { + flex: 1 1 auto; width: 100%; padding: 0.35rem 0.6rem; border: 1px solid var(--border-subtle); @@ -201,12 +208,20 @@ font-size: 0.95rem; } -.selection-assignment__header input:focus-visible { +.selection-assignment__form input:focus-visible { outline: none; border-color: color-mix(in oklch, var(--accent) 60%, transparent); box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent); } +.selection-assignment__add { + align-self: stretch; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 2rem; +} + .selection-assignment__list { max-height: max(240px, 50vh); overflow-y: auto; diff --git a/frontend/src/styles/sidebar/sidebar.css b/frontend/src/styles/sidebar/sidebar.css index a1a8479..0b8ae83 100644 --- a/frontend/src/styles/sidebar/sidebar.css +++ b/frontend/src/styles/sidebar/sidebar.css @@ -247,6 +247,7 @@ .menu__list { overflow-y: auto; + max-height: min(20rem, 60vh); padding: 0.25rem; display: flex; flex-direction: column;