mirror of
https://github.com/devonthesofa/obsidian-note-status.git
synced 2026-07-22 12:30:24 +00:00
fix: improve ui; clean unused component
This commit is contained in:
parent
ddcb49ab84
commit
d47c76c0ca
10 changed files with 75 additions and 377 deletions
|
|
@ -74,8 +74,6 @@ export const TemplateEditor: React.FC<TemplateEditorProps> = ({
|
|||
id: template?.id || "", // Will be generated in integration
|
||||
name: templateName.trim(),
|
||||
description: templateDescription.trim(),
|
||||
isPredefined: template?.isPredefined || false,
|
||||
isEnabled: template?.isEnabled ?? true,
|
||||
statuses: statuses,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ export const TemplateEditorModal: React.FC<TemplateEditorModalProps> = ({
|
|||
name: name.trim(),
|
||||
description: description.trim(),
|
||||
statuses: statusesWithTemplateId,
|
||||
isCustom: true,
|
||||
};
|
||||
|
||||
onSave(savedTemplate);
|
||||
|
|
|
|||
|
|
@ -78,15 +78,18 @@ export const TemplateItem: React.FC<TemplateItemProps> = ({
|
|||
title="Edit template"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||
<path d="m18.5 2.5 4 4L13 16l-4-4L18.5 2.5z" />
|
||||
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
|
||||
<path d="m15 5 4 4" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
|
|
@ -100,18 +103,21 @@ export const TemplateItem: React.FC<TemplateItemProps> = ({
|
|||
title="Delete template"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="3,6 5,6 21,6" />
|
||||
<path d="M19,6l-2,14a2,2,0,0,1-2,2H9a2,2,0,0,1-2-2L5,6" />
|
||||
<path d="M10,11V17" />
|
||||
<path d="M14,11V17" />
|
||||
<path d="M9,6V4a2,2,0,0,1,2-2h2a2,2,0,0,1,2,2V6" />
|
||||
<path d="M10 11v6" />
|
||||
<path d="M14 11v6" />
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
|
||||
<path d="M3 6h18" />
|
||||
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ import {
|
|||
generateTemplateId,
|
||||
isTemplateNameUnique,
|
||||
} from "@/utils/templateUtils";
|
||||
import {
|
||||
DEFAULT_ENABLED_TEMPLATES,
|
||||
PREDEFINED_TEMPLATES,
|
||||
} from "@/constants/predefinedTemplates";
|
||||
|
||||
interface TemplateSettingsProps {
|
||||
settings: PluginSettings;
|
||||
onChange: (key: keyof PluginSettings, value: unknown) => void;
|
||||
templates: StatusTemplate[];
|
||||
}
|
||||
|
||||
export const TemplateSettings: React.FC<TemplateSettingsProps> = ({
|
||||
|
|
@ -124,8 +127,8 @@ export const TemplateSettings: React.FC<TemplateSettingsProps> = ({
|
|||
);
|
||||
if (!confirmed) return;
|
||||
|
||||
onChange("templates", []);
|
||||
onChange("enabledTemplates", ["colorful"]);
|
||||
onChange("templates", [...PREDEFINED_TEMPLATES]);
|
||||
onChange("enabledTemplates", [...DEFAULT_ENABLED_TEMPLATES]);
|
||||
}, [onChange]);
|
||||
|
||||
const handleCancelEditor = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
import React from "react";
|
||||
import { StatusTemplate } from "../../types/pluginSettings";
|
||||
import { StatusDisplay } from "../atoms/StatusDisplay";
|
||||
|
||||
interface UnifiedTemplateItemProps {
|
||||
template: StatusTemplate;
|
||||
onToggle: (templateId: string, enabled: boolean) => void;
|
||||
onEdit: (template: StatusTemplate) => void;
|
||||
onDelete: (templateId: string) => void;
|
||||
onReset: (templateId: string) => void;
|
||||
onDuplicate: (templateId: string) => void;
|
||||
}
|
||||
|
||||
export const UnifiedTemplateItem: React.FC<UnifiedTemplateItemProps> = ({
|
||||
template,
|
||||
onToggle,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onReset,
|
||||
onDuplicate,
|
||||
}) => {
|
||||
const handleToggle = () => {
|
||||
onToggle(template.id, !template.isEnabled);
|
||||
};
|
||||
|
||||
const handleEdit = () => {
|
||||
onEdit(template);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (
|
||||
confirm(
|
||||
`Are you sure you want to delete the template "${template.name}"?`,
|
||||
)
|
||||
) {
|
||||
onDelete(template.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (
|
||||
confirm(
|
||||
`Reset "${template.name}" to its original state? This will lose any customizations.`,
|
||||
)
|
||||
) {
|
||||
onReset(template.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDuplicate = () => {
|
||||
onDuplicate(template.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`unified-template-item ${template.isEnabled ? "unified-template-item--enabled" : ""}`}
|
||||
>
|
||||
<div className="unified-template-item__main" onClick={handleToggle}>
|
||||
<div className="unified-template-item__checkbox-container">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="unified-template-item__checkbox"
|
||||
checked={template.isEnabled || false}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="unified-template-item__content">
|
||||
<div className="unified-template-item__header">
|
||||
<span className="unified-template-item__name">
|
||||
{template.name}
|
||||
</span>
|
||||
{template.isPredefined && (
|
||||
<span className="unified-template-item__badge">
|
||||
Predefined
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="unified-template-item__description">
|
||||
{template.description}
|
||||
</div>
|
||||
|
||||
<div className="unified-template-item__statuses">
|
||||
{template.statuses.map((status, index) => (
|
||||
<StatusDisplay
|
||||
key={index}
|
||||
status={status}
|
||||
variant="template"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="unified-template-item__actions">
|
||||
<button
|
||||
className="unified-template-item__action-btn unified-template-item__action-btn--edit"
|
||||
onClick={handleEdit}
|
||||
aria-label="Edit template"
|
||||
title="Edit template"
|
||||
>
|
||||
✏️
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="unified-template-item__action-btn unified-template-item__action-btn--duplicate"
|
||||
onClick={handleDuplicate}
|
||||
aria-label="Duplicate template"
|
||||
title="Duplicate template"
|
||||
>
|
||||
📋
|
||||
</button>
|
||||
|
||||
{template.isPredefined && (
|
||||
<button
|
||||
className="unified-template-item__action-btn unified-template-item__action-btn--reset"
|
||||
onClick={handleReset}
|
||||
aria-label="Reset to default"
|
||||
title="Reset to default"
|
||||
>
|
||||
🔄
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!template.isPredefined && (
|
||||
<button
|
||||
className="unified-template-item__action-btn unified-template-item__action-btn--delete"
|
||||
onClick={handleDelete}
|
||||
aria-label="Delete template"
|
||||
title="Delete template"
|
||||
>
|
||||
🗑️
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
import React, { useState, useCallback } from "react";
|
||||
import { PluginSettings, StatusTemplate } from "../../types/pluginSettings";
|
||||
import { TemplateIntegration } from "../../integrations/templates/templateIntegration";
|
||||
import { UnifiedTemplateItem } from "./UnifiedTemplateItem";
|
||||
import { TemplateEditor } from "./TemplateEditor";
|
||||
|
||||
interface UnifiedTemplateSettingsProps {
|
||||
settings: PluginSettings;
|
||||
onChange: (key: keyof PluginSettings, value: unknown) => void;
|
||||
}
|
||||
|
||||
export const UnifiedTemplateSettings: React.FC<
|
||||
UnifiedTemplateSettingsProps
|
||||
> = ({ settings, onChange }) => {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [editingTemplate, setEditingTemplate] =
|
||||
useState<StatusTemplate | null>(null);
|
||||
|
||||
const handleCreateTemplate = useCallback(() => {
|
||||
setIsCreating(true);
|
||||
}, []);
|
||||
|
||||
const handleEditTemplate = useCallback((template: StatusTemplate) => {
|
||||
setEditingTemplate(template);
|
||||
}, []);
|
||||
|
||||
const handleDeleteTemplate = useCallback(
|
||||
(templateId: string) => {
|
||||
const success = TemplateIntegration.deleteTemplate(templateId);
|
||||
if (success) {
|
||||
// Trigger settings refresh
|
||||
onChange("templates", TemplateIntegration.getTemplates());
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleResetTemplate = useCallback(
|
||||
(templateId: string) => {
|
||||
const success =
|
||||
TemplateIntegration.resetPredefinedTemplate(templateId);
|
||||
if (success) {
|
||||
// Trigger settings refresh
|
||||
onChange("templates", TemplateIntegration.getTemplates());
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleDuplicateTemplate = useCallback(
|
||||
(templateId: string) => {
|
||||
const newTemplate =
|
||||
TemplateIntegration.duplicateTemplate(templateId);
|
||||
if (newTemplate) {
|
||||
// Trigger settings refresh
|
||||
onChange("templates", TemplateIntegration.getTemplates());
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleToggleTemplate = useCallback(
|
||||
(templateId: string, isEnabled: boolean) => {
|
||||
TemplateIntegration.toggleTemplate(templateId, isEnabled);
|
||||
// Trigger settings refresh
|
||||
onChange("templates", TemplateIntegration.getTemplates());
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleSaveTemplate = useCallback(
|
||||
(template: StatusTemplate) => {
|
||||
if (editingTemplate) {
|
||||
// Update existing template
|
||||
const success = TemplateIntegration.updateTemplate(
|
||||
editingTemplate.id,
|
||||
template,
|
||||
);
|
||||
if (success) {
|
||||
onChange("templates", TemplateIntegration.getTemplates());
|
||||
}
|
||||
} else {
|
||||
// Create new template
|
||||
TemplateIntegration.createTemplate(
|
||||
template.name,
|
||||
template.description,
|
||||
template.statuses,
|
||||
);
|
||||
onChange("templates", TemplateIntegration.getTemplates());
|
||||
}
|
||||
|
||||
setIsCreating(false);
|
||||
setEditingTemplate(null);
|
||||
},
|
||||
[editingTemplate, onChange],
|
||||
);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
setIsCreating(false);
|
||||
setEditingTemplate(null);
|
||||
}, []);
|
||||
|
||||
const predefinedTemplates = TemplateIntegration.getPredefinedTemplates();
|
||||
const customTemplates = TemplateIntegration.getCustomTemplates();
|
||||
|
||||
return (
|
||||
<div className="unified-template-settings">
|
||||
<div className="setting-item-info">
|
||||
<div className="setting-item-name">Template Management</div>
|
||||
<div className="setting-item-description">
|
||||
Manage predefined and custom status templates. You can edit,
|
||||
enable/disable, and reset predefined templates.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(isCreating || editingTemplate) && (
|
||||
<TemplateEditor
|
||||
template={editingTemplate}
|
||||
onSave={handleSaveTemplate}
|
||||
onCancel={handleCancel}
|
||||
existingTemplateNames={(settings.templates || [])
|
||||
.filter((t) => t.id !== editingTemplate?.id)
|
||||
.map((t) => t.name.toLowerCase())}
|
||||
settings={settings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isCreating && !editingTemplate && (
|
||||
<>
|
||||
<div className="template-actions-bar">
|
||||
<button
|
||||
className="mod-cta"
|
||||
onClick={handleCreateTemplate}
|
||||
>
|
||||
Create New Template
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{predefinedTemplates.length > 0 && (
|
||||
<div className="template-section">
|
||||
<h4 className="template-section__title">
|
||||
Predefined Templates
|
||||
</h4>
|
||||
<p className="template-section__description">
|
||||
Built-in templates that can be customized,
|
||||
enabled/disabled, or reset to defaults.
|
||||
</p>
|
||||
<div className="template-list">
|
||||
{predefinedTemplates.map((template) => (
|
||||
<UnifiedTemplateItem
|
||||
key={template.id}
|
||||
template={template}
|
||||
onToggle={handleToggleTemplate}
|
||||
onEdit={handleEditTemplate}
|
||||
onDelete={handleDeleteTemplate}
|
||||
onReset={handleResetTemplate}
|
||||
onDuplicate={handleDuplicateTemplate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{customTemplates.length > 0 && (
|
||||
<div className="template-section">
|
||||
<h4 className="template-section__title">
|
||||
Custom Templates
|
||||
</h4>
|
||||
<p className="template-section__description">
|
||||
Your custom templates that you've created.
|
||||
</p>
|
||||
<div className="template-list">
|
||||
{customTemplates.map((template) => (
|
||||
<UnifiedTemplateItem
|
||||
key={template.id}
|
||||
template={template}
|
||||
onToggle={handleToggleTemplate}
|
||||
onEdit={handleEditTemplate}
|
||||
onDelete={handleDeleteTemplate}
|
||||
onReset={handleResetTemplate}
|
||||
onDuplicate={handleDuplicateTemplate}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{customTemplates.length === 0 && (
|
||||
<div className="template-section">
|
||||
<div className="template-list--empty">
|
||||
<p>No custom templates created yet.</p>
|
||||
<p>
|
||||
Click "Create New Template" to get started.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -31,7 +31,8 @@ export class CommandsIntegration {
|
|||
({ key }) => {
|
||||
if (
|
||||
key === "quickStatusCommands" ||
|
||||
key === "useMultipleStatuses"
|
||||
key === "useMultipleStatuses" ||
|
||||
key === "templates"
|
||||
) {
|
||||
this.commandsService.destroy();
|
||||
/// BUG: if removed a command will persist because is not removed, you need the oldStates to send it to be disabled // const oldValue = this.settings[key]; // TODO: Send the old value
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ export class FileExplorerIntegration implements IElementProcessor {
|
|||
key === "strictStatuses" ||
|
||||
key === "fileExplorerIconPosition" ||
|
||||
key === "unknownStatusIcon" ||
|
||||
key === "unknownStatusColor"
|
||||
key === "unknownStatusColor" ||
|
||||
key === "templates"
|
||||
) {
|
||||
this.destroy();
|
||||
this.integrate().catch((r) => console.error(r));
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export class StatusBarIntegration {
|
|||
if (key === "showStatusBar") {
|
||||
this.render(); // INFO: Force a render to set disabled or enabled
|
||||
}
|
||||
if (key === "enabledTemplates") {
|
||||
if (key === "enabledTemplates" || key === "templates") {
|
||||
this.handleActiveFileChange().catch(console.error); // INFO: Force a re-read of the statuses and render
|
||||
}
|
||||
if (key === "autoHideStatusBar") {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@
|
|||
align-items: center;
|
||||
gap: var(--size-2-2);
|
||||
padding: var(--size-2-3) var(--size-4-3);
|
||||
background: var(--background-modifier-border);
|
||||
border: 1px solid var(--background-modifier-border-hover);
|
||||
background: var(--interactive-normal);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
color: var(--text-normal);
|
||||
cursor: pointer;
|
||||
|
|
@ -37,8 +37,8 @@
|
|||
}
|
||||
|
||||
.template-reset-btn:hover {
|
||||
background: var(--background-modifier-border-hover);
|
||||
color: var(--text-accent);
|
||||
background: var(--interactive-hover);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
/* Template Sections */
|
||||
|
|
@ -58,8 +58,9 @@
|
|||
border-bottom: 2px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.template-section-title svg {
|
||||
.template-section-title .lucide {
|
||||
opacity: 0.8;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.template-list {
|
||||
|
|
@ -82,12 +83,20 @@
|
|||
|
||||
.template-item:hover {
|
||||
background: var(--background-modifier-hover);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
border-color: var(--background-modifier-border-hover);
|
||||
box-shadow: 0 2px 8px var(--shadow-color);
|
||||
}
|
||||
|
||||
.template-item.enabled {
|
||||
background: var(--background-modifier-success);
|
||||
border-color: var(--interactive-success);
|
||||
background: var(--background-modifier-hover);
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 0 0 1px var(--interactive-accent);
|
||||
}
|
||||
|
||||
.template-item.enabled:hover {
|
||||
box-shadow:
|
||||
0 2px 8px var(--shadow-color),
|
||||
0 0 0 1px var(--interactive-accent);
|
||||
}
|
||||
|
||||
.template-item-content {
|
||||
|
|
@ -109,6 +118,9 @@
|
|||
}
|
||||
|
||||
.template-custom-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--size-2-1);
|
||||
padding: var(--size-2-1) var(--size-2-2);
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
|
|
@ -119,6 +131,11 @@
|
|||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.template-custom-badge .lucide {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
/* Template Item Actions */
|
||||
.template-item-actions {
|
||||
display: flex;
|
||||
|
|
@ -143,6 +160,7 @@
|
|||
cursor: pointer;
|
||||
transition: all var(--anim-duration-fast) ease;
|
||||
color: var(--text-muted);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.template-action-btn:hover {
|
||||
|
|
@ -150,8 +168,8 @@
|
|||
}
|
||||
|
||||
.template-action-btn--edit {
|
||||
background: var(--background-modifier-border-hover);
|
||||
color: var(--text-accent);
|
||||
background: var(--interactive-normal);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.template-action-btn--edit:hover {
|
||||
|
|
@ -160,7 +178,7 @@
|
|||
}
|
||||
|
||||
.template-action-btn--delete {
|
||||
background: var(--background-modifier-error);
|
||||
background: var(--interactive-normal);
|
||||
color: var(--text-error);
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +187,11 @@
|
|||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.template-action-btn .lucide {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* Template Editor Modal */
|
||||
.template-editor-modal {
|
||||
background: var(--background-primary);
|
||||
|
|
@ -206,10 +229,18 @@
|
|||
}
|
||||
|
||||
.template-editor-add-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--size-2-2);
|
||||
align-self: flex-start;
|
||||
margin-top: var(--size-4-2);
|
||||
}
|
||||
|
||||
.template-editor-add-status .lucide {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.template-editor-modal__actions {
|
||||
display: flex;
|
||||
gap: var(--size-4-2);
|
||||
|
|
@ -227,16 +258,16 @@
|
|||
font-weight: var(--font-medium);
|
||||
cursor: pointer;
|
||||
transition: all var(--anim-duration-fast) ease;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.template-editor-modal__actions button:not(.mod-cta) {
|
||||
background: var(--background-modifier-border);
|
||||
border: 1px solid var(--background-modifier-border-hover);
|
||||
background: var(--interactive-normal);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.template-editor-modal__actions button:not(.mod-cta):hover {
|
||||
background: var(--background-modifier-border-hover);
|
||||
background: var(--interactive-hover);
|
||||
}
|
||||
|
||||
.template-editor-modal__actions .mod-cta:disabled {
|
||||
|
|
|
|||
Loading…
Reference in a new issue