mirror of
https://github.com/devonthesofa/obsidian-note-status.git
synced 2026-07-22 12:30:24 +00:00
Merge pull request #100 from devonthesofa/feature/improve-status-group
improve status group UI
This commit is contained in:
commit
bed56422aa
6 changed files with 96 additions and 84 deletions
|
|
@ -1,7 +1,5 @@
|
|||
import React, { useCallback } from "react";
|
||||
import { FileItem } from "../GroupedStatusView";
|
||||
import { SelectableListItem } from "../../atoms/SelectableListItem";
|
||||
|
||||
interface FileListProps {
|
||||
files: FileItem[];
|
||||
groupKey: string;
|
||||
|
|
@ -48,21 +46,23 @@ export const FileList = ({
|
|||
<div className="grouped-status-files">
|
||||
<div className="grouped-status-files-list" onScroll={handleScroll}>
|
||||
{visibleFiles.map((file) => (
|
||||
<SelectableListItem
|
||||
<div
|
||||
key={file.id}
|
||||
className="tree-item nav-file grouped-status-file-item"
|
||||
onClick={() => handleFileClick(file)}
|
||||
className="grouped-status-file-item"
|
||||
title={file.path}
|
||||
>
|
||||
<div>
|
||||
<span className="grouped-status-file-name">
|
||||
{file.name}
|
||||
</span>
|
||||
<span className="grouped-status-file-path">
|
||||
{file.path}
|
||||
</span>
|
||||
<div className="tree-item-self is-clickable nav-file-title">
|
||||
<div className="tree-item-inner nav-file-title-content grouped-status-file-content">
|
||||
<div className="grouped-status-file-name">
|
||||
{file.name}
|
||||
</div>
|
||||
<div className="grouped-status-file-path">
|
||||
{file.path}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SelectableListItem>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{hasMoreItems && (
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ export const StatusGroup = ({
|
|||
}, [onToggle]);
|
||||
|
||||
return (
|
||||
<div className="grouped-status-group">
|
||||
<div className="grouped-status-group tree-item">
|
||||
<div
|
||||
className="grouped-status-group__header"
|
||||
className="grouped-status-group__header tree-item-self is-clickable"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<div className="grouped-status-group__status">
|
||||
<div className="tree-item-inner grouped-status-group__status">
|
||||
<StatusDisplay
|
||||
status={
|
||||
{
|
||||
|
|
@ -78,14 +78,16 @@ export const StatusGroup = ({
|
|||
</div>
|
||||
|
||||
{isExpanded && (
|
||||
<FileList
|
||||
files={files}
|
||||
groupKey={groupKey}
|
||||
loadedCount={loadedCount}
|
||||
onFileClick={onFileClick}
|
||||
onScroll={onScroll}
|
||||
onLoadMore={onLoadMore}
|
||||
/>
|
||||
<div className="tree-item-children">
|
||||
<FileList
|
||||
files={files}
|
||||
groupKey={groupKey}
|
||||
loadedCount={loadedCount}
|
||||
onFileClick={onFileClick}
|
||||
onScroll={onScroll}
|
||||
onLoadMore={onLoadMore}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback } from "react";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { GroupLabel } from "@/components/atoms/GroupLabel";
|
||||
import { CollapsibleCounter } from "@/components/atoms/CollapsibleCounter";
|
||||
import { FilesByStatus, FileItem, StatusItem } from "../GroupedStatusView";
|
||||
|
|
@ -51,6 +51,11 @@ export const TagSection = ({
|
|||
[onToggleFiles],
|
||||
);
|
||||
|
||||
const statusOrder = useMemo(
|
||||
() => Array.from(statusMap.keys()),
|
||||
[statusMap],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grouped-status-tag-section">
|
||||
<div className="grouped-status-tag-header" onClick={handleToggle}>
|
||||
|
|
@ -64,32 +69,38 @@ export const TagSection = ({
|
|||
|
||||
{isExpanded && (
|
||||
<div className="grouped-status-tag-content">
|
||||
{Object.entries(statusGroups).map(([statusName, files]) => {
|
||||
if (files.length === 0) return null;
|
||||
{Object.entries(statusGroups)
|
||||
.sort(
|
||||
([statusA], [statusB]) =>
|
||||
statusOrder.indexOf(statusA) -
|
||||
statusOrder.indexOf(statusB),
|
||||
)
|
||||
.map(([statusName, files]) => {
|
||||
if (files.length === 0) return null;
|
||||
|
||||
const status = statusMap.get(statusName);
|
||||
if (!status) return null;
|
||||
const status = statusMap.get(statusName);
|
||||
if (!status) return null;
|
||||
|
||||
const groupKey = `${tag}-${statusName}`;
|
||||
const filesExpanded = expandedFiles.has(groupKey);
|
||||
const loadedCount = getLoadedCount(groupKey);
|
||||
const groupKey = `${tag}-${statusName}`;
|
||||
const filesExpanded = expandedFiles.has(groupKey);
|
||||
const loadedCount = getLoadedCount(groupKey);
|
||||
|
||||
return (
|
||||
<StatusGroup
|
||||
key={statusName}
|
||||
statusName={statusName}
|
||||
status={status}
|
||||
files={files}
|
||||
tag={tag}
|
||||
isExpanded={filesExpanded}
|
||||
loadedCount={loadedCount}
|
||||
onToggle={() => handleToggleFiles(groupKey)}
|
||||
onFileClick={onFileClick}
|
||||
onScroll={onScroll}
|
||||
onLoadMore={onLoadMore}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<StatusGroup
|
||||
key={statusName}
|
||||
statusName={statusName}
|
||||
status={status}
|
||||
files={files}
|
||||
tag={tag}
|
||||
isExpanded={filesExpanded}
|
||||
loadedCount={loadedCount}
|
||||
onToggle={() => handleToggleFiles(groupKey)}
|
||||
onFileClick={onFileClick}
|
||||
onScroll={onScroll}
|
||||
onLoadMore={onLoadMore}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -78,25 +78,18 @@
|
|||
|
||||
/* Status Group */
|
||||
.grouped-status-group {
|
||||
margin-bottom: var(--size-4-2);
|
||||
background: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
margin-bottom: 2px;
|
||||
border-radius: var(--radius-m);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow var(--anim-duration-fast) ease;
|
||||
}
|
||||
|
||||
.grouped-status-group:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.grouped-status-group__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--size-4-2) var(--size-4-3);
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-s);
|
||||
transition: background var(--anim-duration-fast) ease;
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -105,8 +98,8 @@
|
|||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.grouped-status-group__header:focus {
|
||||
outline: 2px solid var(--color-accent);
|
||||
.grouped-status-group__header:focus-visible {
|
||||
outline: 2px solid var(--interactive-accent);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
|
|
@ -138,40 +131,46 @@
|
|||
|
||||
/* Files List */
|
||||
.grouped-status-files {
|
||||
background: var(--background-secondary);
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
margin-bottom: var(--size-2-2);
|
||||
}
|
||||
|
||||
.grouped-status-files-list {
|
||||
max-height: 300px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.grouped-status-file-item {
|
||||
padding: var(--size-2-3) var(--size-4-3);
|
||||
cursor: pointer;
|
||||
transition: background var(--anim-duration-fast) ease;
|
||||
border-bottom: 1px solid var(--background-modifier-border-hover);
|
||||
border-radius: var(--radius-s);
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.grouped-status-file-item:hover {
|
||||
.grouped-status-file-item:hover .tree-item-self {
|
||||
background: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.grouped-status-file-item:last-child {
|
||||
border-bottom: none;
|
||||
.grouped-status-file-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.grouped-status-file-name {
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-normal);
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.grouped-status-file-path {
|
||||
font-size: var(--font-ui-small);
|
||||
font-size: var(--font-ui-smaller);
|
||||
color: var(--text-muted);
|
||||
opacity: 0.8;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Load More */
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@ import { NoteStatus } from "./noteStatus";
|
|||
|
||||
export type StatusFrontmatterMapping =
|
||||
| {
|
||||
id: string;
|
||||
scope: "template";
|
||||
templateId: string;
|
||||
frontmatterKeys: string[];
|
||||
}
|
||||
id: string;
|
||||
scope: "template";
|
||||
templateId: string;
|
||||
frontmatterKeys: string[];
|
||||
}
|
||||
| {
|
||||
id: string;
|
||||
scope: "status";
|
||||
templateId?: string;
|
||||
statusName: string;
|
||||
frontmatterKeys: string[];
|
||||
};
|
||||
id: string;
|
||||
scope: "status";
|
||||
templateId?: string;
|
||||
statusName: string;
|
||||
frontmatterKeys: string[];
|
||||
};
|
||||
|
||||
export interface StatusTemplate {
|
||||
id: string;
|
||||
|
|
@ -37,9 +37,9 @@ export type SyncGroup =
|
|||
|
||||
export type PluginSettings = {
|
||||
fileExplorerIconPosition:
|
||||
| "absolute-right"
|
||||
| "file-name-left"
|
||||
| "file-name-right";
|
||||
| "absolute-right"
|
||||
| "file-name-left"
|
||||
| "file-name-right";
|
||||
fileExplorerIconFrame: "always" | "never";
|
||||
fileExplorerIconColorMode: "status" | "theme";
|
||||
statusColors: Record<string, string>;
|
||||
|
|
|
|||
Loading…
Reference in a new issue