From 1c9f8a254071f3c9a87bb3ebabaabf9d97669b9a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 31 Jan 2026 11:39:48 +0200 Subject: [PATCH] chore(collections): add collection toolbar to all collections --- .../src/widgets/collections/board/index.tsx | 114 +++++++++--------- .../src/widgets/collections/geomap/index.tsx | 40 +++--- .../collections/legacy/ListOrGridView.tsx | 5 + .../collections/presentation/index.tsx | 48 ++++---- .../src/widgets/collections/table/index.tsx | 35 +++--- .../note_bars/CollectionProperties.css | 4 +- 6 files changed, 133 insertions(+), 113 deletions(-) diff --git a/apps/client/src/widgets/collections/board/index.tsx b/apps/client/src/widgets/collections/board/index.tsx index 39e715f97c..455b952b44 100644 --- a/apps/client/src/widgets/collections/board/index.tsx +++ b/apps/client/src/widgets/collections/board/index.tsx @@ -1,20 +1,23 @@ -import { Dispatch, StateUpdater, useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks"; -import { ViewModeProps } from "../interface"; import "./index.css"; -import { ColumnMap, getBoardData } from "./data"; + +import { createContext, TargetedKeyboardEvent } from "preact"; +import { Dispatch, StateUpdater, useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks"; + +import FNote from "../../../entities/fnote"; +import { t } from "../../../services/i18n"; +import toast from "../../../services/toast"; +import CollectionProperties from "../../note_bars/CollectionProperties"; +import FormTextArea from "../../react/FormTextArea"; +import FormTextBox from "../../react/FormTextBox"; import { useNoteLabelBoolean, useNoteLabelWithDefault, useTriliumEvent } from "../../react/hooks"; import Icon from "../../react/Icon"; -import { t } from "../../../services/i18n"; -import Api from "./api"; -import FormTextBox from "../../react/FormTextBox"; -import { createContext, TargetedKeyboardEvent } from "preact"; -import { onWheelHorizontalScroll } from "../../widget_utils"; -import Column from "./column"; -import BoardApi from "./api"; -import FormTextArea from "../../react/FormTextArea"; -import FNote from "../../../entities/fnote"; import NoteAutocomplete from "../../react/NoteAutocomplete"; -import toast from "../../../services/toast"; +import { onWheelHorizontalScroll } from "../../widget_utils"; +import { ViewModeProps } from "../interface"; +import Api from "./api"; +import BoardApi from "./api"; +import Column from "./column"; +import { ColumnMap, getBoardData } from "./data"; export interface BoardViewData { columns?: BoardColumnData[]; @@ -145,7 +148,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC const insertBefore = mouseX < columnMiddle; // Calculate the target position - let targetIndex = insertBefore ? index : index + 1; + const targetIndex = insertBefore ? index : index + 1; setColumnDropPosition(targetIndex); }, [draggedColumn]); @@ -163,6 +166,7 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC className="board-view" onWheel={onWheelHorizontalScroll} > + {byColumn && columns &&
}
- ) + ); } function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMode: boolean }) { @@ -218,26 +222,26 @@ function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMo tabIndex={300} > {!isCreatingNewColumn - ? <> - {" "} - {t("board_view.add-column")} - - : ( - { - const created = await api.addNewColumn(columnName); - if (!created) { - toast.showMessage(t("board_view.column-already-exists"), undefined, "bx bx-duplicate"); - } - }} - dismiss={() => setIsCreatingNewColumn(false)} - isNewItem - mode={isInRelationMode ? "relation" : "normal"} - /> - )} + ? <> + {" "} + {t("board_view.add-column")} + + : ( + { + const created = await api.addNewColumn(columnName); + if (!created) { + toast.showMessage(t("board_view.column-already-exists"), undefined, "bx bx-duplicate"); + } + }} + dismiss={() => setIsCreatingNewColumn(false)} + isNewItem + mode={isInRelationMode ? "relation" : "normal"} + /> + )} - ) + ); } export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, isNewItem }: { @@ -302,26 +306,26 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, is onBlur={onBlur} /> ); - } else { - return ( - { - if (e.key === "Escape") { - dismiss(); - } - }} - onBlur={() => dismiss()} - noteIdChanged={(newValue) => { - save(newValue); - dismiss(); - }} - /> - ); } + return ( + { + if (e.key === "Escape") { + dismiss(); + } + }} + onBlur={() => dismiss()} + noteIdChanged={(newValue) => { + save(newValue); + dismiss(); + }} + /> + ); + } diff --git a/apps/client/src/widgets/collections/geomap/index.tsx b/apps/client/src/widgets/collections/geomap/index.tsx index 6b91d4c8e6..d962c12fef 100644 --- a/apps/client/src/widgets/collections/geomap/index.tsx +++ b/apps/client/src/widgets/collections/geomap/index.tsx @@ -1,24 +1,27 @@ -import Map from "./map"; import "./index.css"; -import { ViewModeProps } from "../interface"; -import { useNoteBlob, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useNoteTreeDrag, useSpacedUpdate, useTriliumEvent } from "../../react/hooks"; -import { DEFAULT_MAP_LAYER_NAME } from "./map_layer"; + import { divIcon, GPXOptions, LatLng, LeafletMouseEvent } from "leaflet"; -import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks"; -import Marker, { GpxTrack } from "./marker"; -import froca from "../../../services/froca"; -import FNote from "../../../entities/fnote"; import markerIcon from "leaflet/dist/images/marker-icon.png"; import markerIconShadow from "leaflet/dist/images/marker-shadow.png"; +import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks"; + import appContext from "../../../components/app_context"; -import { createNewNote, moveMarker } from "./api"; -import openContextMenu, { openMapContextMenu } from "./context_menu"; -import toast from "../../../services/toast"; +import FNote from "../../../entities/fnote"; +import branches from "../../../services/branches"; +import froca from "../../../services/froca"; import { t } from "../../../services/i18n"; import server from "../../../services/server"; -import branches from "../../../services/branches"; -import TouchBar, { TouchBarButton, TouchBarLabel, TouchBarSlider } from "../../react/TouchBar"; +import toast from "../../../services/toast"; +import CollectionProperties from "../../note_bars/CollectionProperties"; +import { useNoteBlob, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useNoteTreeDrag, useSpacedUpdate, useTriliumEvent } from "../../react/hooks"; import { ParentComponent } from "../../react/react_utils"; +import TouchBar, { TouchBarButton, TouchBarLabel, TouchBarSlider } from "../../react/TouchBar"; +import { ViewModeProps } from "../interface"; +import { createNewNote, moveMarker } from "./api"; +import openContextMenu, { openMapContextMenu } from "./context_menu"; +import Map from "./map"; +import { DEFAULT_MAP_LAYER_NAME } from "./map_layer"; +import Marker, { GpxTrack } from "./marker"; const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659]; const DEFAULT_ZOOM = 2; @@ -50,7 +53,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM } }, 5000); - useEffect(() => { froca.getNotes(noteIds).then(setNotes) }, [ noteIds ]); + useEffect(() => { froca.getNotes(noteIds).then(setNotes); }, [ noteIds ]); useEffect(() => { if (!note) return; @@ -60,7 +63,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM // Note creation. useTriliumEvent("geoMapCreateChildNote", () => { - toast.showPersistent({ + toast.showPersistent({ icon: "plus", id: "geo-new-note", title: "New note", @@ -130,6 +133,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM return (
+ { coordinates !== undefined && zoom !== undefined && + />; } function NoteGpxTrack({ note }: { note: FNote }) { @@ -238,7 +242,7 @@ function NoteGpxTrack({ note }: { note: FNote }) { color: note.getLabelValue("color") ?? "blue" } }), [ color, iconClass ]); - return xmlString && + return xmlString && ; } function buildIcon(bxIconClass: string, colorClass?: string, title?: string, noteIdLink?: string, archived?: boolean) { @@ -292,5 +296,5 @@ function GeoMapTouchBar({ state, map }: { state: State, map: L.Map | null | unde enabled={state === State.Normal} /> - ) + ); } diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx index 40e6ae0bc6..80f791a07d 100644 --- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx +++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx @@ -7,6 +7,7 @@ import attribute_renderer from "../../../services/attribute_renderer"; import content_renderer from "../../../services/content_renderer"; import { t } from "../../../services/i18n"; import link from "../../../services/link"; +import CollectionProperties from "../../note_bars/CollectionProperties"; import { useImperativeSearchHighlighlighting, useNoteLabel, useNoteLabelBoolean } from "../../react/hooks"; import Icon from "../../react/Icon"; import NoteLink from "../../react/NoteLink"; @@ -22,6 +23,8 @@ export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens } return (
+ + { noteIds.length > 0 &&
@@ -48,6 +51,8 @@ export function GridView({ note, noteIds: unfilteredNoteIds, highlightedTokens } return (
+ +
diff --git a/apps/client/src/widgets/collections/presentation/index.tsx b/apps/client/src/widgets/collections/presentation/index.tsx index 0a3d36f65d..28fd18fa0a 100644 --- a/apps/client/src/widgets/collections/presentation/index.tsx +++ b/apps/client/src/widgets/collections/presentation/index.tsx @@ -1,18 +1,21 @@ -import { ViewModeMedia, ViewModeProps } from "../interface"; +import "./index.css"; + +import { RefObject } from "preact"; import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks"; import Reveal from "reveal.js"; import slideBaseStylesheet from "reveal.js/dist/reveal.css?raw"; -import slideCustomStylesheet from "./slidejs.css?raw"; -import { buildPresentationModel, PresentationModel, PresentationSlideBaseModel } from "./model"; -import ShadowDom from "../../react/ShadowDom"; -import ActionButton from "../../react/ActionButton"; -import "./index.css"; -import { RefObject } from "preact"; + import { openInCurrentNoteContext } from "../../../components/note_context"; -import { useNoteLabelWithDefault, useTriliumEvent } from "../../react/hooks"; -import { t } from "../../../services/i18n"; -import { DEFAULT_THEME, loadPresentationTheme } from "./themes"; import FNote from "../../../entities/fnote"; +import { t } from "../../../services/i18n"; +import CollectionProperties from "../../note_bars/CollectionProperties"; +import ActionButton from "../../react/ActionButton"; +import { useNoteLabelWithDefault, useTriliumEvent } from "../../react/hooks"; +import ShadowDom from "../../react/ShadowDom"; +import { ViewModeMedia, ViewModeProps } from "../interface"; +import { buildPresentationModel, PresentationModel, PresentationSlideBaseModel } from "./model"; +import slideCustomStylesheet from "./slidejs.css?raw"; +import { DEFAULT_THEME, loadPresentationTheme } from "./themes"; export default function PresentationView({ note, noteIds, media, onReady, onProgressChanged }: ViewModeProps<{}>) { const [ presentation, setPresentation ] = useState(); @@ -52,13 +55,14 @@ export default function PresentationView({ note, noteIds, media, onReady, onProg if (media === "screen") { return ( <> + {content} - ) + ); } else if (media === "print") { // Printing needs a query parameter that is read by Reveal.js. const url = new URL(window.location.href); @@ -143,7 +147,7 @@ function ButtonOverlay({ containerRef, api }: { containerRef: RefObject
- ) + ); } function Presentation({ presentation, setApi } : { presentation: PresentationModel, setApi: (api: Reveal.Api | undefined) => void }) { @@ -179,7 +183,7 @@ function Presentation({ presentation, setApi } : { presentation: PresentationMod api.destroy(); setRevealApi(undefined); setApi(undefined); - } + }; }, []); useEffect(() => { @@ -191,19 +195,19 @@ function Presentation({ presentation, setApi } : { presentation: PresentationMod
{presentation.slides?.map(slide => { if (!slide.verticalSlides) { - return - } else { - return ( -
- - {slide.verticalSlides.map(slide => )} -
- ); + return ; } + return ( +
+ + {slide.verticalSlides.map(slide => )} +
+ ); + })}
- ) + ); } diff --git a/apps/client/src/widgets/collections/table/index.tsx b/apps/client/src/widgets/collections/table/index.tsx index d557f12d33..d01f293d62 100644 --- a/apps/client/src/widgets/collections/table/index.tsx +++ b/apps/client/src/widgets/collections/table/index.tsx @@ -1,20 +1,23 @@ +import "./index.css"; + import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks"; -import { ViewModeProps } from "../interface"; -import { TableData } from "./rows"; -import { useLegacyWidget } from "../../react/hooks"; -import Tabulator from "./tabulator"; -import { Tabulator as VanillaTabulator, SortModule, FormatModule, InteractionModule, EditModule, ResizeColumnsModule, FrozenColumnsModule, PersistenceModule, MoveColumnsModule, MoveRowsModule, DataTreeModule, Options, RowComponent} from 'tabulator-tables'; -import { useContextMenu } from "./context_menu"; -import { ParentComponent } from "../../react/react_utils"; +import { DataTreeModule, EditModule, FormatModule, FrozenColumnsModule, InteractionModule, MoveColumnsModule, MoveRowsModule, Options, PersistenceModule, ResizeColumnsModule, RowComponent,SortModule, Tabulator as VanillaTabulator} from 'tabulator-tables'; + import FNote from "../../../entities/fnote"; import { t } from "../../../services/i18n"; -import Button from "../../react/Button"; -import "./index.css"; -import useRowTableEditing from "./row_editing"; -import useColTableEditing from "./col_editing"; -import AttributeDetailWidget from "../../attribute_widgets/attribute_detail"; import SpacedUpdate from "../../../services/spaced_update"; +import AttributeDetailWidget from "../../attribute_widgets/attribute_detail"; +import CollectionProperties from "../../note_bars/CollectionProperties"; +import Button from "../../react/Button"; +import { useLegacyWidget } from "../../react/hooks"; +import { ParentComponent } from "../../react/react_utils"; +import { ViewModeProps } from "../interface"; +import useColTableEditing from "./col_editing"; +import { useContextMenu } from "./context_menu"; import useData, { TableConfig } from "./data"; +import useRowTableEditing from "./row_editing"; +import { TableData } from "./rows"; +import Tabulator from "./tabulator"; export default function TableView({ note, noteIds, notePath, viewConfig, saveConfig }: ViewModeProps) { const tabulatorRef = useRef(null); @@ -36,7 +39,7 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon dataTreeChildIndent: 20, dataTreeExpandElement: ``, dataTreeCollapseElement: `` - } + }; }, [ hasChildren ]); const rowFormatter = useCallback((row: RowComponent) => { @@ -46,6 +49,8 @@ export default function TableView({ note, noteIds, notePath, viewConfig, saveCon return (
+ + {rowData !== undefined && persistenceProps && ( <> - ) + ); } function TableFooter({ note }: { note: FNote }) { @@ -84,7 +89,7 @@ function TableFooter({ note }: { note: FNote }) {
- ) + ); } function usePersistence(viewConfig: TableConfig | null | undefined, saveConfig: (newConfig: TableConfig) => void) { diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.css b/apps/client/src/widgets/note_bars/CollectionProperties.css index bf06067f5f..5d76a25fe8 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.css +++ b/apps/client/src/widgets/note_bars/CollectionProperties.css @@ -30,12 +30,10 @@ @media (max-width: 991px) { flex-wrap: wrap; - padding: 0.55em 1em; - margin: 0; + margin-bottom: 0.5em; >div { flex-grow: 1; - justify-content: center; } } }