diff --git a/src/apps/annotation-image/Toolbar/MoreTools.tsx b/src/apps/annotation-image/Toolbar/MoreTools.tsx new file mode 100644 index 00000000..829e4aba --- /dev/null +++ b/src/apps/annotation-image/Toolbar/MoreTools.tsx @@ -0,0 +1,60 @@ +import { DotsThreeVertical } from '@phosphor-icons/react'; +import type { PresentUser } from '@annotorious/react'; +import * as Popover from '@radix-ui/react-popover'; +import { ColorCodingSelector, ColorLegend } from '@components/AnnotationDesktop'; +import { PrivacySelector, type PrivacyMode } from '@components/PrivacySelector'; +import type { DocumentLayer, DocumentWithContext, Translations, VocabularyTerm } from 'src/Types'; + +interface MoreToolsProps { + + document: DocumentWithContext; + + i18n: Translations; + + layers?: DocumentLayer[]; + + layerNames: Map; + + present: PresentUser[]; + + privacy: PrivacyMode; + + tagVocabulary?: VocabularyTerm[]; + + onChangePrivacy(mode: PrivacyMode): void; + +} + +export const MoreTools = (props: MoreToolsProps) => { + + return ( + + + + + + + + + + + + + + + ) + +} \ No newline at end of file diff --git a/src/apps/annotation-image/Toolbar/Toolbar.tsx b/src/apps/annotation-image/Toolbar/Toolbar.tsx index 45a44253..21dc13ef 100644 --- a/src/apps/annotation-image/Toolbar/Toolbar.tsx +++ b/src/apps/annotation-image/Toolbar/Toolbar.tsx @@ -4,10 +4,11 @@ import type { SupabaseAnnotation } from '@recogito/annotorious-supabase'; import { Extension, usePlugins } from '@components/Plugins'; import { PresenceStack } from '@components/Presence'; import type { DocumentLayer, DocumentWithContext, Policies, Translations, VocabularyTerm } from 'src/Types'; -import { ColorCodingSelector, ColorLegend, DeleteSelected, ErrorBadge, useColorCoding } from '@components/AnnotationDesktop'; +import { ColorCodingSelector, ColorLegend, DeleteSelected, ErrorBadge, useCollapsibleToolbar, useColorCoding } from '@components/AnnotationDesktop'; import { PrivacySelector, type PrivacyMode } from '@components/PrivacySelector'; import { useFilter } from '@components/AnnotationDesktop/FilterPanel/FilterState'; import { Polygon, Rectangle } from './Icons'; +import { MoreTools } from './MoreTools'; import { Chats, Cursor, @@ -77,6 +78,8 @@ export const Toolbar = (props: ToolbarProps) => { const colorCoding = useColorCoding(); + const { ref, collapsed } = useCollapsibleToolbar(); + useEffect(() => { if (colorCoding?.style) props.onChangeStyle(colorCoding.style); @@ -85,7 +88,9 @@ export const Toolbar = (props: ToolbarProps) => { }, [colorCoding]); return ( -
+
{
-
+
{contextName ? ( -

- - - {contextName} - - - / - {props.document.name} -

+ <> + + +

+ +
{contextName}
+
+ / +
{props.document.name}
+

+ ) : (

- {props.document.name} +
{props.document.name}

)}
@@ -130,15 +136,19 @@ export const Toolbar = (props: ToolbarProps) => { )}
-
+
{!props.isLocked && ( <> - - -
+ {!collapsed && ( + <> + + +
+ + )} -
- {!props.isLocked && ( l.is_active)} @@ -182,16 +190,34 @@ export const Toolbar = (props: ToolbarProps) => { policies={props.policies} /> )} - + {collapsed && ( + + )} + +
- + {!collapsed && ( + <> + + + + + )}
diff --git a/src/apps/annotation-text/Toolbar/MoreTools.tsx b/src/apps/annotation-text/Toolbar/MoreTools.tsx new file mode 100644 index 00000000..54cce024 --- /dev/null +++ b/src/apps/annotation-text/Toolbar/MoreTools.tsx @@ -0,0 +1,56 @@ +import { DotsThreeVertical } from '@phosphor-icons/react'; +import type { PresentUser } from '@annotorious/react'; +import * as Popover from '@radix-ui/react-popover'; +import { ColorCodingSelector, ColorLegend } from '@components/AnnotationDesktop'; +import type { DocumentLayer, DocumentWithContext, Translations, VocabularyTerm } from 'src/Types'; +import { PDFControls } from './PDFControls'; + +interface MoreToolsProps { + + document: DocumentWithContext; + + layers?: DocumentLayer[]; + + layerNames: Map; + + i18n: Translations; + + isPDF: boolean; + + present: PresentUser[]; + + tagVocabulary: VocabularyTerm[]; + +} + +export const MoreTools = (props: MoreToolsProps) => { + + return ( + + + + + + + + +
+ + + + + + + ) + +} \ No newline at end of file diff --git a/src/apps/annotation-text/Toolbar/Toolbar.tsx b/src/apps/annotation-text/Toolbar/Toolbar.tsx index 4146beb6..571fd6ee 100644 --- a/src/apps/annotation-text/Toolbar/Toolbar.tsx +++ b/src/apps/annotation-text/Toolbar/Toolbar.tsx @@ -1,13 +1,14 @@ import { useEffect } from 'react'; import { Chats, FunnelSimple, GraduationCap } from '@phosphor-icons/react'; import type { Color, PresentUser } from '@annotorious/react'; -import { ColorCodingSelector, DeleteSelected, ColorLegend, ErrorBadge, useColorCoding, useFilter } from '@components/AnnotationDesktop'; +import { ColorCodingSelector, DeleteSelected, ColorLegend, ErrorBadge, useColorCoding, useFilter, useCollapsibleToolbar } from '@components/AnnotationDesktop'; import { Extension, usePlugins } from '@components/Plugins'; import { PresenceStack } from '@components/Presence'; import { type PrivacyMode, PrivacySelector } from '@components/PrivacySelector'; import { PDFControls } from './PDFControls'; import type { DocumentLayer, DocumentWithContext, Policies, Translations, VocabularyTerm } from 'src/Types'; import type { SupabaseAnnotation } from '@recogito/annotorious-supabase'; +import { MoreTools } from './MoreTools'; interface ToolbarProps { @@ -65,6 +66,8 @@ export const Toolbar = (props: ToolbarProps) => { const colorCoding = useColorCoding(); + const { ref, collapsed } = useCollapsibleToolbar(); + useEffect(() => { if (colorCoding?.style) props.onChangeStyle(colorCoding.style); @@ -73,7 +76,9 @@ export const Toolbar = (props: ToolbarProps) => { }, [colorCoding]); return ( -
+
{
-
+
{contextName ? ( -

- - - {contextName} - - - / - {props.document.name} -

+ <> + + +

+ +
{contextName}
+
+ / +
{props.document.name}
+

+ ) : (

- {props.document.name} +
{props.document.name}

)}
@@ -118,22 +124,21 @@ export const Toolbar = (props: ToolbarProps) => { )}
-
+
{!props.isLocked && ( - <> - + + )} -
- + {!collapsed && ( +
)} - {isPDF && ( + {(isPDF && !collapsed) && (
-
)} @@ -144,16 +149,22 @@ export const Toolbar = (props: ToolbarProps) => { policies={props.policies} /> )} - + {!collapsed && ( + <> +
- + + + + + )}
@@ -167,7 +178,18 @@ export const Toolbar = (props: ToolbarProps) => { )} -
+ {collapsed ? ( + + ) : ( +
+ )} {plugins.map(plugin => ( { onClick={isMine ? onDeleteSelection : () => setPromptOverride(true)}> - -
) diff --git a/src/components/AnnotationDesktop/index.ts b/src/components/AnnotationDesktop/index.ts index 487884ea..9e6ba80b 100644 --- a/src/components/AnnotationDesktop/index.ts +++ b/src/components/AnnotationDesktop/index.ts @@ -11,5 +11,6 @@ export * from './FilterPanel'; export * from './SelectionURLState'; export * from './UndoStack'; export * from './useAnnotationsViewUIState'; +export * from './useCollapsibleToolbar'; export * from './useLayerNames'; export * from './utils'; diff --git a/src/components/AnnotationDesktop/useCollapsibleToolbar.ts b/src/components/AnnotationDesktop/useCollapsibleToolbar.ts new file mode 100644 index 00000000..496e75c0 --- /dev/null +++ b/src/components/AnnotationDesktop/useCollapsibleToolbar.ts @@ -0,0 +1,38 @@ +import { useEffect, useRef, useState } from 'react'; + +export const useCollapsibleToolbar = () => { + + const ref = useRef(null); + + const [windowWidth, setWindowWidth] = useState(window.innerWidth); + + const [breakpoint, setBreakpoint] = useState(0); + + const [collapsed, setCollapsed] = useState(false); + + useEffect(() => { + const onResize = () => setWindowWidth(window.innerWidth); + + window.addEventListener('resize', onResize); + + return () => { + window.removeEventListener('resize', onResize); + } + }, []); + + useEffect(() => { + if (!ref.current) return; + + const shouldCollapse = ref.current ? ref.current.scrollWidth > ref.current.clientWidth : false; + + if (shouldCollapse && !collapsed) { + setBreakpoint(windowWidth); + setCollapsed(true); + } else if (!shouldCollapse && windowWidth > breakpoint) { + setCollapsed(false); + } + }, [windowWidth]); + + return { ref, collapsed }; + +} \ No newline at end of file diff --git a/src/themes/default/annotation/toolbar.css b/src/themes/default/annotation/toolbar.css index 7ac74cb6..c4550a0b 100644 --- a/src/themes/default/annotation/toolbar.css +++ b/src/themes/default/annotation/toolbar.css @@ -4,6 +4,7 @@ box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.18), 0 0 1px rgba(0, 0, 0, 0.3); display: flex; justify-content: space-between; + overflow-x: hidden; padding: 0.375rem 0.75rem; position: relative; z-index: 10; @@ -12,40 +13,21 @@ .anno-toolbar-slot { align-items: center; display: flex; + flex-shrink: 0; gap: 0.25rem; + overflow: hidden; } -.anno-toolbar h1 { - align-items: center; - display: flex; - font-size: var(--font-tiny); - font-family: Inter; - gap: 0.5rem; - margin: 0; - padding-left: 0.75rem; - white-space: nowrap; -} - -.anno-toolbar h1 a[href] { - align-items: center; - color: var(--font-dark); - display: inline-flex; - gap: 0.5rem; -} - -.anno-toolbar h1 span { - color: var(--font-light); - font-weight: 400; -} - -.anno-toolbar .assignment-icon { - color: var(--font-dark); +.anno-toolbar-slot-center.collapsed { + flex-grow: 1; + justify-content: flex-end; } .anno-toolbar button { border-radius: 50%; color: var(--font-dark); flex: 0 0 auto; + flex-shrink: 0; height: 36px; vertical-align: bottom; width: 36px; @@ -86,6 +68,59 @@ .anno-toolbar-group { align-items: center; display: flex; + flex-shrink: 0; +} + +.anno-toolbar .anno-toolbar-title { + align-items: center; + color: var(--font-dark); + display: flex; + gap: 0.25rem; + padding-left: 0.75rem; + white-space: nowrap; +} + +.anno-toolbar .anno-toolbar-title h1 { + align-items: center; + display: flex; + font-family: Inter; + font-size: var(--font-tiny); + font-weight: 400; + gap: 0.25rem; + white-space: nowrap; +} + +.anno-toolbar .anno-toolbar-title h1 .document-title.in-assignment { + color: var(--font-light); + max-width: 25vw; + min-width: 140px; + overflow: hidden; + text-overflow: ellipsis; +} + +.anno-toolbar .anno-toolbar-title h1 a { + display: flex; + color: var(--font-dark); + font-weight: 500; + overflow: hidden; +} + +.anno-toolbar .anno-toolbar-title h1 a > * { + flex-shrink: 0; +} + +.anno-toolbar .anno-toolbar-title h1 div { + max-width: 25vw; + min-width: 140px; + overflow: hidden; + text-overflow: ellipsis; +} + +.anno-toolbar .anno-toolbar-title h1 a > div { + max-width: 14vw; + min-width: 60px; + overflow: hidden; + text-overflow: ellipsis; } .anno-toolbar .anno-toolbar-right button:hover { @@ -127,8 +162,16 @@ border-color: rgba(0, 0, 0, 0.08); border-width: 0 2px 0 0; display: inline-block; + flex-shrink: 0; height: 16px; margin: 0 4px; right: 0; } +.popover-content.anno-more-tools { + align-items: center; + display: flex; + padding: 0.375rem 0.75rem; + width: fit-content; + white-space: nowrap; +}