mirror of
https://github.com/zadam/trilium.git
synced 2025-12-17 21:04:24 +01:00
feat(layout/search_definition): integrate view options directly in search parameters
This commit is contained in:
parent
6b9b9a96c3
commit
a7ca839afb
@ -889,7 +889,8 @@
|
|||||||
"search_parameters": "Search Parameters",
|
"search_parameters": "Search Parameters",
|
||||||
"unknown_search_option": "Unknown search option {{searchOptionName}}",
|
"unknown_search_option": "Unknown search option {{searchOptionName}}",
|
||||||
"search_note_saved": "Search note has been saved into {{- notePathTitle}}",
|
"search_note_saved": "Search note has been saved into {{- notePathTitle}}",
|
||||||
"actions_executed": "Actions have been executed."
|
"actions_executed": "Actions have been executed.",
|
||||||
|
"view_options": "View options:"
|
||||||
},
|
},
|
||||||
"similar_notes": {
|
"similar_notes": {
|
||||||
"title": "Similar Notes",
|
"title": "Similar Notes",
|
||||||
|
|||||||
@ -4,29 +4,8 @@ body.experimental-feature-new-layout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.title-actions {
|
.title-actions {
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
gap: 0.25em;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
max-width: unset;
|
|
||||||
padding-inline-start: 15px;
|
padding-inline-start: 15px;
|
||||||
|
padding-top: 1em;
|
||||||
padding-bottom: 0.2em;
|
padding-bottom: 0.2em;
|
||||||
font-size: 0.8em;
|
|
||||||
|
|
||||||
.collapsible-title {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
input.form-control {
|
|
||||||
padding: 2px 8px;
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.spacer {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
apps/client/src/widgets/note_bars/CollectionProperties.css
Normal file
20
apps/client/src/widgets/note_bars/CollectionProperties.css
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.collection-properties {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25em;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: unset;
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
input.form-control {
|
||||||
|
padding: 2px 8px;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,14 @@
|
|||||||
|
import "./CollectionProperties.css";
|
||||||
|
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { useContext } from "preact/hooks";
|
import { useContext } from "preact/hooks";
|
||||||
import { Fragment } from "preact/jsx-runtime";
|
import { Fragment } from "preact/jsx-runtime";
|
||||||
|
|
||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
|
import { getHelpUrlForNote } from "../../services/in_app_help";
|
||||||
|
import { openInAppHelpFromUrl } from "../../services/utils";
|
||||||
import { ViewTypeOptions } from "../collections/interface";
|
import { ViewTypeOptions } from "../collections/interface";
|
||||||
|
import ActionButton from "../react/ActionButton";
|
||||||
import Dropdown from "../react/Dropdown";
|
import Dropdown from "../react/Dropdown";
|
||||||
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
|
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
|
||||||
import FormTextBox from "../react/FormTextBox";
|
import FormTextBox from "../react/FormTextBox";
|
||||||
@ -12,9 +17,6 @@ import Icon from "../react/Icon";
|
|||||||
import { ParentComponent } from "../react/react_utils";
|
import { ParentComponent } from "../react/react_utils";
|
||||||
import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config";
|
import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config";
|
||||||
import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab";
|
import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab";
|
||||||
import ActionButton from "../react/ActionButton";
|
|
||||||
import { getHelpUrlForNote } from "../../services/in_app_help";
|
|
||||||
import { openInAppHelpFromUrl } from "../../services/utils";
|
|
||||||
|
|
||||||
const ICON_MAPPINGS: Record<ViewTypeOptions, string> = {
|
const ICON_MAPPINGS: Record<ViewTypeOptions, string> = {
|
||||||
grid: "bx bxs-grid",
|
grid: "bx bxs-grid",
|
||||||
@ -30,12 +32,12 @@ export default function CollectionProperties({ note }: { note: FNote }) {
|
|||||||
const [ viewType, setViewType ] = useViewType(note);
|
const [ viewType, setViewType ] = useViewType(note);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="collection-properties">
|
||||||
<ViewTypeSwitcher viewType={viewType} setViewType={setViewType} />
|
<ViewTypeSwitcher viewType={viewType} setViewType={setViewType} />
|
||||||
<ViewOptions note={note} viewType={viewType} />
|
<ViewOptions note={note} viewType={viewType} />
|
||||||
<div className="spacer" />
|
<div className="spacer" />
|
||||||
<HelpButton note={note} />
|
<HelpButton note={note} />
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,9 +189,9 @@ function ComboBoxPropertyView({ note, property }: { note: FNote, property: Combo
|
|||||||
{index < property.options.length - 1 && <FormDropdownDivider />}
|
{index < property.options.length - 1 && <FormDropdownDivider />}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return renderItem(option);
|
|
||||||
}
|
}
|
||||||
|
return renderItem(option);
|
||||||
|
|
||||||
})}
|
})}
|
||||||
</FormDropdownSubmenu>
|
</FormDropdownSubmenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
|
|||||||
title: t("book_properties.book_properties"),
|
title: t("book_properties.book_properties"),
|
||||||
icon: "bx bx-book",
|
icon: "bx bx-book",
|
||||||
content: CollectionPropertiesTab,
|
content: CollectionPropertiesTab,
|
||||||
show: ({ note }) => !isNewLayout && note?.type === "book" || note?.type === "search",
|
show: ({ note }) => !isNewLayout && (note?.type === "book" || note?.type === "search"),
|
||||||
toggleCommand: "toggleRibbonTabBookProperties"
|
toggleCommand: "toggleRibbonTabBookProperties"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
import FormTextArea from "../react/FormTextArea";
|
import { AttributeType } from "@triliumnext/commons";
|
||||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
|
||||||
import FormSelect from "../react/FormSelect";
|
|
||||||
import Icon from "../react/Icon";
|
|
||||||
import FormTextBox from "../react/FormTextBox";
|
|
||||||
import { ComponentChildren, VNode } from "preact";
|
import { ComponentChildren, VNode } from "preact";
|
||||||
|
import { useEffect, useMemo, useRef } from "preact/hooks";
|
||||||
|
|
||||||
|
import appContext from "../../components/app_context";
|
||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
import { removeOwnedAttributesByNameOrType } from "../../services/attributes";
|
import { removeOwnedAttributesByNameOrType } from "../../services/attributes";
|
||||||
import { AttributeType } from "@triliumnext/commons";
|
|
||||||
import { useNoteLabel, useNoteRelation, useSpacedUpdate, useTooltip } from "../react/hooks";
|
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import { useEffect, useMemo, useRef } from "preact/hooks";
|
|
||||||
import appContext from "../../components/app_context";
|
|
||||||
import server from "../../services/server";
|
import server from "../../services/server";
|
||||||
|
import FormSelect from "../react/FormSelect";
|
||||||
|
import FormTextArea from "../react/FormTextArea";
|
||||||
|
import FormTextBox from "../react/FormTextBox";
|
||||||
import HelpRemoveButtons from "../react/HelpRemoveButtons";
|
import HelpRemoveButtons from "../react/HelpRemoveButtons";
|
||||||
|
import { useNoteLabel, useNoteRelation, useSpacedUpdate, useTooltip } from "../react/hooks";
|
||||||
|
import Icon from "../react/Icon";
|
||||||
|
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||||
|
|
||||||
export interface SearchOption {
|
export interface SearchOption {
|
||||||
attributeName: string;
|
attributeName: string;
|
||||||
@ -134,7 +135,7 @@ function SearchOption({ note, title, titleIcon, children, help, attributeName, a
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchStringOption({ note, refreshResults, error, ...restProps }: SearchOptionProps) {
|
function SearchStringOption({ note, refreshResults, error, ...restProps }: SearchOptionProps) {
|
||||||
@ -210,7 +211,7 @@ function SearchStringOption({ note, refreshResults, error, ...restProps }: Searc
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SearchOption>
|
</SearchOption>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchScriptOption({ note, ...restProps }: SearchOptionProps) {
|
function SearchScriptOption({ note, ...restProps }: SearchOptionProps) {
|
||||||
@ -232,7 +233,7 @@ function SearchScriptOption({ note, ...restProps }: SearchOptionProps) {
|
|||||||
noteIdChanged={noteId => setSearchScript(noteId ?? "root")}
|
noteIdChanged={noteId => setSearchScript(noteId ?? "root")}
|
||||||
placeholder={t("search_script.placeholder")}
|
placeholder={t("search_script.placeholder")}
|
||||||
/>
|
/>
|
||||||
</SearchOption>
|
</SearchOption>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AncestorOption({ note, ...restProps}: SearchOptionProps) {
|
function AncestorOption({ note, ...restProps}: SearchOptionProps) {
|
||||||
@ -245,9 +246,9 @@ function AncestorOption({ note, ...restProps}: SearchOptionProps) {
|
|||||||
{ value: "eq1", label: `${t("ancestor.depth_eq", { count: 1 })} (${t("ancestor.direct_children")})` }
|
{ value: "eq1", label: `${t("ancestor.depth_eq", { count: 1 })} (${t("ancestor.direct_children")})` }
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i=2; i<=9; i++) options.push({ value: "eq" + i, label: t("ancestor.depth_eq", { count: i }) });
|
for (let i=2; i<=9; i++) options.push({ value: `eq${ i}`, label: t("ancestor.depth_eq", { count: i }) });
|
||||||
for (let i=0; i<=9; i++) options.push({ value: "gt" + i, label: t("ancestor.depth_gt", { count: i }) });
|
for (let i=0; i<=9; i++) options.push({ value: `gt${ i}`, label: t("ancestor.depth_gt", { count: i }) });
|
||||||
for (let i=2; i<=9; i++) options.push({ value: "lt" + i, label: t("ancestor.depth_lt", { count: i }) });
|
for (let i=2; i<=9; i++) options.push({ value: `lt${ i}`, label: t("ancestor.depth_lt", { count: i }) });
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}, []);
|
}, []);
|
||||||
@ -279,7 +280,7 @@ function FastSearchOption({ ...restProps }: SearchOptionProps) {
|
|||||||
titleIcon="bx bx-run" title={t("fast_search.fast_search")}
|
titleIcon="bx bx-run" title={t("fast_search.fast_search")}
|
||||||
help={t("fast_search.description")}
|
help={t("fast_search.description")}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DebugOption({ ...restProps }: SearchOptionProps) {
|
function DebugOption({ ...restProps }: SearchOptionProps) {
|
||||||
@ -290,14 +291,14 @@ function DebugOption({ ...restProps }: SearchOptionProps) {
|
|||||||
{t("debug.access_info")}
|
{t("debug.access_info")}
|
||||||
</>}
|
</>}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function IncludeArchivedNotesOption({ ...restProps }: SearchOptionProps) {
|
function IncludeArchivedNotesOption({ ...restProps }: SearchOptionProps) {
|
||||||
return <SearchOption
|
return <SearchOption
|
||||||
titleIcon="bx bx-archive" title={t("include_archived_notes.include_archived_notes")}
|
titleIcon="bx bx-archive" title={t("include_archived_notes.include_archived_notes")}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function OrderByOption({ note, ...restProps }: SearchOptionProps) {
|
function OrderByOption({ note, ...restProps }: SearchOptionProps) {
|
||||||
@ -340,7 +341,7 @@ function OrderByOption({ note, ...restProps }: SearchOptionProps) {
|
|||||||
{ value: "desc", title: t("order_by.desc") }
|
{ value: "desc", title: t("order_by.desc") }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</SearchOption>
|
</SearchOption>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function LimitOption({ note, defaultValue, ...restProps }: SearchOptionProps) {
|
function LimitOption({ note, defaultValue, ...restProps }: SearchOptionProps) {
|
||||||
@ -356,5 +357,5 @@ function LimitOption({ note, defaultValue, ...restProps }: SearchOptionProps) {
|
|||||||
type="number" min="1" step="1"
|
type="number" min="1" step="1"
|
||||||
currentValue={limit ?? defaultValue} onChange={setLimit}
|
currentValue={limit ?? defaultValue} onChange={setLimit}
|
||||||
/>
|
/>
|
||||||
</SearchOption>
|
</SearchOption>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import appContext from "../../components/app_context";
|
|||||||
import FNote from "../../entities/fnote";
|
import FNote from "../../entities/fnote";
|
||||||
import attributes from "../../services/attributes";
|
import attributes from "../../services/attributes";
|
||||||
import bulk_action, { ACTION_GROUPS } from "../../services/bulk_action";
|
import bulk_action, { ACTION_GROUPS } from "../../services/bulk_action";
|
||||||
|
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
|
||||||
import froca from "../../services/froca";
|
import froca from "../../services/froca";
|
||||||
import { t } from "../../services/i18n";
|
import { t } from "../../services/i18n";
|
||||||
import server from "../../services/server";
|
import server from "../../services/server";
|
||||||
@ -15,6 +16,7 @@ import tree from "../../services/tree";
|
|||||||
import { getErrorMessage } from "../../services/utils";
|
import { getErrorMessage } from "../../services/utils";
|
||||||
import ws from "../../services/ws";
|
import ws from "../../services/ws";
|
||||||
import RenameNoteBulkAction from "../bulk_actions/note/rename_note";
|
import RenameNoteBulkAction from "../bulk_actions/note/rename_note";
|
||||||
|
import CollectionProperties from "../note_bars/CollectionProperties";
|
||||||
import Button from "../react/Button";
|
import Button from "../react/Button";
|
||||||
import Dropdown from "../react/Dropdown";
|
import Dropdown from "../react/Dropdown";
|
||||||
import { FormListHeader, FormListItem } from "../react/FormList";
|
import { FormListHeader, FormListItem } from "../react/FormList";
|
||||||
@ -24,6 +26,8 @@ import { ParentComponent } from "../react/react_utils";
|
|||||||
import { TabContext } from "./ribbon-interface";
|
import { TabContext } from "./ribbon-interface";
|
||||||
import { SEARCH_OPTIONS, SearchOption } from "./SearchDefinitionOptions";
|
import { SEARCH_OPTIONS, SearchOption } from "./SearchDefinitionOptions";
|
||||||
|
|
||||||
|
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
|
||||||
|
|
||||||
export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabContext, "note" | "ntxId" | "hidden">) {
|
export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabContext, "note" | "ntxId" | "hidden">) {
|
||||||
const parentComponent = useContext(ParentComponent);
|
const parentComponent = useContext(ParentComponent);
|
||||||
const [ searchOptions, setSearchOptions ] = useState<{ availableOptions: SearchOption[], activeOptions: SearchOption[] }>();
|
const [ searchOptions, setSearchOptions ] = useState<{ availableOptions: SearchOption[], activeOptions: SearchOption[] }>();
|
||||||
@ -78,7 +82,7 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
|
|||||||
return (
|
return (
|
||||||
<div className="search-definition-widget">
|
<div className="search-definition-widget">
|
||||||
<div className="search-settings">
|
<div className="search-settings">
|
||||||
{note && !hidden &&
|
{note && !hidden && (
|
||||||
<table className="search-setting-table">
|
<table className="search-setting-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@ -111,6 +115,11 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
|
|||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
/>;
|
/>;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{isNewLayout && <tr className="view-options">
|
||||||
|
<td className="title-column">{t("search_definition.view_options")}</td>
|
||||||
|
<td><CollectionProperties note={note} /></td>
|
||||||
|
</tr>}
|
||||||
</tbody>
|
</tbody>
|
||||||
<BulkActionsList note={note} />
|
<BulkActionsList note={note} />
|
||||||
<tbody className="search-actions">
|
<tbody className="search-actions">
|
||||||
@ -156,7 +165,7 @@ export default function SearchDefinitionTab({ note, ntxId, hidden }: Pick<TabCon
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user