routing added
This commit is contained in:
+155
-16
@@ -21,16 +21,93 @@ const AIHelpView = lazy(() => import('./views/AIHelpView').then(m => ({ default:
|
|||||||
const NotepadView = lazy(() => import('./views/NotepadView').then(m => ({ default: m.NotepadView })));
|
const NotepadView = lazy(() => import('./views/NotepadView').then(m => ({ default: m.NotepadView })));
|
||||||
const QuickEntryForm = lazy(() => import('./components/QuickEntryForm').then(m => ({ default: m.QuickEntryForm })));
|
const QuickEntryForm = lazy(() => import('./components/QuickEntryForm').then(m => ({ default: m.QuickEntryForm })));
|
||||||
|
|
||||||
|
const DEFAULT_VIEW = "critical";
|
||||||
|
const routeToView: Record<string, string> = {
|
||||||
|
critical: "critical",
|
||||||
|
focus: "critical",
|
||||||
|
urgency: "urgency",
|
||||||
|
priority: "priority",
|
||||||
|
matrix: "matrix",
|
||||||
|
snowball: "snowball",
|
||||||
|
"dig-in": "dig_in",
|
||||||
|
"dig_in": "dig_in",
|
||||||
|
progress: "progress",
|
||||||
|
settings: "settings",
|
||||||
|
help: "help",
|
||||||
|
ask: "help_ai",
|
||||||
|
"help-ai": "help_ai",
|
||||||
|
"help_ai": "help_ai",
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewToRoute: Record<string, string> = {
|
||||||
|
critical: "focus",
|
||||||
|
urgency: "urgency",
|
||||||
|
priority: "priority",
|
||||||
|
matrix: "matrix",
|
||||||
|
snowball: "snowball",
|
||||||
|
dig_in: "dig-in",
|
||||||
|
progress: "progress",
|
||||||
|
settings: "settings",
|
||||||
|
help: "help",
|
||||||
|
help_ai: "ask",
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeHash = (hash: string) => {
|
||||||
|
if (hash === "#help" || hash.startsWith("#help/")) {
|
||||||
|
return `#/${hash.slice(1)}`;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseHashRoute = (hash: string): { view: string; sectionId?: string } | null => {
|
||||||
|
const normalizedHash = normalizeHash(hash);
|
||||||
|
if (!normalizedHash.startsWith("#/")) return null;
|
||||||
|
|
||||||
|
const routePath = normalizedHash.slice(2);
|
||||||
|
if (!routePath || routePath.startsWith("embed/")) return null;
|
||||||
|
|
||||||
|
const [viewSegment, ...rest] = routePath.split("/");
|
||||||
|
if (decodeURIComponent(viewSegment) === "notes") {
|
||||||
|
return {
|
||||||
|
view: "notes",
|
||||||
|
sectionId: rest.length > 0 ? decodeURIComponent(rest.join("/")) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const view = routeToView[decodeURIComponent(viewSegment)];
|
||||||
|
if (!view) return null;
|
||||||
|
|
||||||
|
if (view === "help") {
|
||||||
|
return {
|
||||||
|
view,
|
||||||
|
sectionId: rest.length > 0 ? decodeURIComponent(rest.join("/")) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { view };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getHashForView = (view: string, sectionId?: string) => {
|
||||||
|
if (view === "notes") {
|
||||||
|
return sectionId ? `#/notes/${encodeURIComponent(sectionId)}` : "#/notes";
|
||||||
|
}
|
||||||
|
const route = viewToRoute[view] || viewToRoute[DEFAULT_VIEW];
|
||||||
|
if (view === "help" && sectionId) {
|
||||||
|
return `#/${route}/${encodeURIComponent(sectionId)}`;
|
||||||
|
}
|
||||||
|
return `#/${route}`;
|
||||||
|
};
|
||||||
|
|
||||||
const App: Component = () => {
|
const App: Component = () => {
|
||||||
// Basic routing state
|
// Basic routing state
|
||||||
const [currentView, setCurrentView] = createSignal("critical");
|
const [currentView, setCurrentView] = createSignal(DEFAULT_VIEW);
|
||||||
const [lastNonAIView, setLastNonAIView] = createSignal("critical");
|
const [lastNonAIView, setLastNonAIView] = createSignal(DEFAULT_VIEW);
|
||||||
const [isAuthenticated, setIsAuthenticated] = createSignal(pb.authStore.isValid);
|
const [isAuthenticated, setIsAuthenticated] = createSignal(pb.authStore.isValid);
|
||||||
const [authUserId, setAuthUserId] = createSignal<string | null>(pb.authStore.model?.id || null);
|
const [authUserId, setAuthUserId] = createSignal<string | null>(pb.authStore.model?.id || null);
|
||||||
const [location, setLocation] = createSignal(window.location.href);
|
const [location, setLocation] = createSignal(window.location.href);
|
||||||
const [isDesktop, setIsDesktop] = createSignal(window.matchMedia("(min-width: 768px)").matches);
|
const [isDesktop, setIsDesktop] = createSignal(window.matchMedia("(min-width: 768px)").matches);
|
||||||
const [aiHelpTip, setAiHelpTip] = createSignal<string | null>(null);
|
const [aiHelpTip, setAiHelpTip] = createSignal<string | null>(null);
|
||||||
const [tipGeneratedForUserId, setTipGeneratedForUserId] = createSignal<string | null>(null);
|
const [tipGeneratedForUserId, setTipGeneratedForUserId] = createSignal<string | null>(null);
|
||||||
|
const [selectedNoteId, setSelectedNoteId] = createSignal<string | null>(null);
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const view = currentView();
|
const view = currentView();
|
||||||
@@ -43,6 +120,21 @@ const App: Component = () => {
|
|||||||
currentView() === "help_ai" && isDesktop() ? lastNonAIView() : currentView()
|
currentView() === "help_ai" && isDesktop() ? lastNonAIView() : currentView()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const syncNotesRoute = (noteId?: string | null, options?: { replace?: boolean }) => {
|
||||||
|
syncUrlToView("notes", { sectionId: noteId || undefined, replace: options?.replace });
|
||||||
|
};
|
||||||
|
|
||||||
|
const openNotes = (noteId?: string | null, options?: { replace?: boolean }) => {
|
||||||
|
setStore('isNotepadMode', true);
|
||||||
|
setSelectedNoteId(noteId || null);
|
||||||
|
syncNotesRoute(noteId, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openTasks = (view?: string, options?: { replace?: boolean }) => {
|
||||||
|
setStore('isNotepadMode', false);
|
||||||
|
navigateToView(view || currentView(), options);
|
||||||
|
};
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const userId = authUserId();
|
const userId = authUserId();
|
||||||
const authenticated = isAuthenticated();
|
const authenticated = isAuthenticated();
|
||||||
@@ -68,21 +160,38 @@ const App: Component = () => {
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const syncUrlToView = (view: string, options?: { sectionId?: string; replace?: boolean }) => {
|
||||||
|
if (isEmbed()) return;
|
||||||
|
|
||||||
|
const nextHash = getHashForView(view, options?.sectionId);
|
||||||
|
if (window.location.hash === nextHash) return;
|
||||||
|
|
||||||
|
if (options?.replace) {
|
||||||
|
const nextUrl = `${window.location.pathname}${window.location.search}${nextHash}`;
|
||||||
|
window.history.replaceState(window.history.state, "", nextUrl);
|
||||||
|
window.dispatchEvent(new HashChangeEvent("hashchange"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.hash = nextHash;
|
||||||
|
};
|
||||||
|
|
||||||
|
const navigateToView = (view: string, options?: { sectionId?: string; replace?: boolean }) => {
|
||||||
|
setCurrentView(view);
|
||||||
|
syncUrlToView(view, options);
|
||||||
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const mediaQuery = window.matchMedia("(min-width: 768px)");
|
const mediaQuery = window.matchMedia("(min-width: 768px)");
|
||||||
const openHelpGuide = (sectionId?: string) => {
|
const openHelpGuide = (sectionId?: string) => {
|
||||||
const nextHash = sectionId ? `#help/${sectionId}` : "#help";
|
|
||||||
if (window.location.hash !== nextHash) {
|
|
||||||
window.location.hash = nextHash;
|
|
||||||
}
|
|
||||||
setStore('isNotepadMode', false);
|
setStore('isNotepadMode', false);
|
||||||
setCurrentView("help");
|
navigateToView("help", { sectionId });
|
||||||
window.dispatchEvent(new CustomEvent("taskgrid:help-target", { detail: { sectionId } }));
|
window.dispatchEvent(new CustomEvent("taskgrid:help-target", { detail: { sectionId } }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const openAIHelp = () => {
|
const openAIHelp = () => {
|
||||||
setStore('isNotepadMode', false);
|
setStore('isNotepadMode', false);
|
||||||
setCurrentView("help_ai");
|
navigateToView("help_ai");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMediaChange = (event: MediaQueryListEvent) => {
|
const handleMediaChange = (event: MediaQueryListEvent) => {
|
||||||
@@ -92,13 +201,39 @@ const App: Component = () => {
|
|||||||
const handleLocChange = () => {
|
const handleLocChange = () => {
|
||||||
setLocation(window.location.href);
|
setLocation(window.location.href);
|
||||||
|
|
||||||
const hash = window.location.hash;
|
if (isEmbed()) return;
|
||||||
if (hash === "#help" || hash.startsWith("#help/")) {
|
|
||||||
setCurrentView("help");
|
const routeState = parseHashRoute(window.location.hash);
|
||||||
|
if (routeState) {
|
||||||
|
if (routeState.view === "notes") {
|
||||||
|
setStore('isNotepadMode', true);
|
||||||
|
setSelectedNoteId(routeState.sectionId || null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStore('isNotepadMode', false);
|
||||||
|
setCurrentView(routeState.view);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedHash = normalizeHash(window.location.hash);
|
||||||
|
if (normalizedHash !== window.location.hash) {
|
||||||
|
const nextUrl = `${window.location.pathname}${window.location.search}${normalizedHash}`;
|
||||||
|
window.history.replaceState(window.history.state, "", nextUrl);
|
||||||
|
setLocation(window.location.href);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.location.hash) {
|
||||||
|
if (store.isNotepadMode) {
|
||||||
|
syncNotesRoute(selectedNoteId(), { replace: true });
|
||||||
|
} else {
|
||||||
|
syncUrlToView(currentView(), { replace: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(window as any).setCurrentView = setCurrentView;
|
(window as any).setCurrentView = navigateToView;
|
||||||
(window as any).openTasGridHelp = openHelpGuide;
|
(window as any).openTasGridHelp = openHelpGuide;
|
||||||
(window as any).openTasGridAIHelp = openAIHelp;
|
(window as any).openTasGridAIHelp = openAIHelp;
|
||||||
|
|
||||||
@@ -186,9 +321,13 @@ const App: Component = () => {
|
|||||||
<Show when={isAuthenticated()} fallback={<AuthCallback onSuccess={() => { }} />}>
|
<Show when={isAuthenticated()} fallback={<AuthCallback onSuccess={() => { }} />}>
|
||||||
<Layout
|
<Layout
|
||||||
currentView={effectiveView()}
|
currentView={effectiveView()}
|
||||||
setView={setCurrentView}
|
setView={navigateToView}
|
||||||
|
selectedNoteId={selectedNoteId()}
|
||||||
|
setSelectedNoteId={setSelectedNoteId}
|
||||||
|
onOpenTasks={openTasks}
|
||||||
|
onOpenNotes={openNotes}
|
||||||
isAIHelpOpen={currentView() === "help_ai" && isDesktop()}
|
isAIHelpOpen={currentView() === "help_ai" && isDesktop()}
|
||||||
onToggleAIHelp={() => setCurrentView(currentView() === "help_ai" ? lastNonAIView() : "help_ai")}
|
onToggleAIHelp={() => navigateToView(currentView() === "help_ai" ? lastNonAIView() : "help_ai")}
|
||||||
aiHelpTip={aiHelpTip()}
|
aiHelpTip={aiHelpTip()}
|
||||||
hideQuickEntry={currentView() === "help_ai" && !isDesktop()}
|
hideQuickEntry={currentView() === "help_ai" && !isDesktop()}
|
||||||
>
|
>
|
||||||
@@ -200,9 +339,9 @@ const App: Component = () => {
|
|||||||
<Show when={effectiveView() === "snowball"}><SnowballView /></Show>
|
<Show when={effectiveView() === "snowball"}><SnowballView /></Show>
|
||||||
<Show when={effectiveView() === "dig_in"}><DigInView /></Show>
|
<Show when={effectiveView() === "dig_in"}><DigInView /></Show>
|
||||||
<Show when={effectiveView() === "progress"}><ProgressView /></Show>
|
<Show when={effectiveView() === "progress"}><ProgressView /></Show>
|
||||||
<Show when={effectiveView() === "settings"}><SettingsView setView={setCurrentView} /></Show>
|
<Show when={effectiveView() === "settings"}><SettingsView setView={navigateToView} /></Show>
|
||||||
<Show when={effectiveView() === "help_ai"}><AIHelpView /></Show>
|
<Show when={effectiveView() === "help_ai"}><AIHelpView /></Show>
|
||||||
<Show when={effectiveView() === "help"}><HelpView onBack={() => setCurrentView("settings")} /></Show>
|
<Show when={effectiveView() === "help"}><HelpView onBack={() => navigateToView("settings")} /></Show>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
+49
-15
@@ -1,6 +1,6 @@
|
|||||||
import { type Component, type JSX, createSignal, createEffect, Show, lazy, Suspense, onMount, onCleanup } from "solid-js";
|
import { type Component, type JSX, createSignal, createEffect, Show, lazy, Suspense, onMount, onCleanup } from "solid-js";
|
||||||
import { Sidebar, BottomNav, ContextSwitcher } from "./Navigation";
|
import { Sidebar, BottomNav, ContextSwitcher } from "./Navigation";
|
||||||
import { store, setStore, activeTaskId, setActiveTaskId, requestImmediateNotesMetadataLoad } from "@/store";
|
import { store, activeTaskId, setActiveTaskId, requestImmediateNotesMetadataLoad, fetchNoteById, updateStoreWithNote } from "@/store";
|
||||||
import { PanelLeftOpen, PanelLeftClose } from "lucide-solid";
|
import { PanelLeftOpen, PanelLeftClose } from "lucide-solid";
|
||||||
|
|
||||||
const TaskDetail = lazy(() => import("./TaskDetail").then(m => ({ default: m.TaskDetail })));
|
const TaskDetail = lazy(() => import("./TaskDetail").then(m => ({ default: m.TaskDetail })));
|
||||||
@@ -15,6 +15,10 @@ interface LayoutProps {
|
|||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
currentView: string;
|
currentView: string;
|
||||||
setView: (v: string) => void;
|
setView: (v: string) => void;
|
||||||
|
selectedNoteId: string | null;
|
||||||
|
setSelectedNoteId: (id: string | null) => void;
|
||||||
|
onOpenTasks: (view?: string, options?: { replace?: boolean }) => void;
|
||||||
|
onOpenNotes: (noteId?: string | null, options?: { replace?: boolean }) => void;
|
||||||
isAIHelpOpen?: boolean;
|
isAIHelpOpen?: boolean;
|
||||||
onToggleAIHelp?: () => void;
|
onToggleAIHelp?: () => void;
|
||||||
aiHelpTip?: string | null;
|
aiHelpTip?: string | null;
|
||||||
@@ -25,21 +29,25 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
const [isSidebarLocked, setIsSidebarLocked] = createSignal(true);
|
const [isSidebarLocked, setIsSidebarLocked] = createSignal(true);
|
||||||
const [isSidebarPeeking, setIsSidebarPeeking] = createSignal(false);
|
const [isSidebarPeeking, setIsSidebarPeeking] = createSignal(false);
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = createSignal(false);
|
const [isDropdownOpen, setIsDropdownOpen] = createSignal(false);
|
||||||
|
const [resolvingRoutedNoteId, setResolvingRoutedNoteId] = createSignal<string | null>(null);
|
||||||
|
|
||||||
// Lift selectedNoteId up to Layout so it can be passed to NotesSidebar and NotepadView
|
const setActiveNoteId = (id: string | null) => {
|
||||||
const [selectedNoteId, setSelectedNoteId] = createSignal<string | null>(null);
|
props.setSelectedNoteId(id);
|
||||||
|
if (store.isNotepadMode) {
|
||||||
|
props.onOpenNotes(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Make it available globally for QuickEntry auto-tagging without circular deps
|
// Make it available globally for QuickEntry auto-tagging without circular deps
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
(window as any)._activeNoteId = selectedNoteId();
|
(window as any)._activeNoteId = props.selectedNoteId;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const handleOpenNote = (e: Event) => {
|
const handleOpenNote = (e: Event) => {
|
||||||
const detail = (e as CustomEvent).detail;
|
const detail = (e as CustomEvent).detail;
|
||||||
if (detail && detail.noteId) {
|
if (detail && detail.noteId) {
|
||||||
setStore('isNotepadMode', true);
|
props.onOpenNotes(detail.noteId);
|
||||||
setSelectedNoteId(detail.noteId);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener('open-note', handleOpenNote);
|
window.addEventListener('open-note', handleOpenNote);
|
||||||
@@ -65,12 +73,38 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const noteId = selectedNoteId();
|
const noteId = props.selectedNoteId;
|
||||||
if (!noteId) return;
|
if (!noteId) return;
|
||||||
|
|
||||||
const noteExists = store.notes.some(note => note.id === noteId);
|
const noteExists = store.notes.some(note => note.id === noteId);
|
||||||
if (!noteExists) {
|
if (noteExists) {
|
||||||
setSelectedNoteId(null);
|
if (resolvingRoutedNoteId() === noteId) {
|
||||||
|
setResolvingRoutedNoteId(null);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (store.isNotesLoading || !store.hasLoadedNotesMetadata) return;
|
||||||
|
if (resolvingRoutedNoteId() === noteId) return;
|
||||||
|
|
||||||
|
setResolvingRoutedNoteId(noteId);
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
const fetched = await fetchNoteById(noteId);
|
||||||
|
if (fetched) {
|
||||||
|
updateStoreWithNote(fetched);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to resolve routed note", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.selectedNoteId === noteId) {
|
||||||
|
setActiveNoteId(null);
|
||||||
|
props.onOpenNotes(null, { replace: true });
|
||||||
|
}
|
||||||
|
setResolvingRoutedNoteId(current => current === noteId ? null : current);
|
||||||
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -95,7 +129,7 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
class={cn(
|
class={cn(
|
||||||
"hidden md:flex flex-col border-r border-border bg-card h-screen fixed left-0 top-0 transition-all duration-200 ease-in-out z-[50] overflow-hidden",
|
"hidden md:flex flex-col border-r border-border bg-card h-screen fixed left-0 top-0 transition-all duration-200 ease-in-out z-[50] overflow-hidden",
|
||||||
isSidebarLocked() ? `${sidebarWidthClass()} translate-x-0` : `${sidebarWidthClass()} -translate-x-full`,
|
isSidebarLocked() ? `${sidebarWidthClass()} translate-x-0` : `${sidebarWidthClass()} -translate-x-full`,
|
||||||
!isSidebarLocked() && (isSidebarPeeking() || (store.isNotepadMode && !selectedNoteId())) && "translate-x-0 shadow-2xl ring-1 ring-border"
|
!isSidebarLocked() && (isSidebarPeeking() || (store.isNotepadMode && !props.selectedNoteId)) && "translate-x-0 shadow-2xl ring-1 ring-border"
|
||||||
)}
|
)}
|
||||||
onMouseEnter={() => !isSidebarLocked() && setIsSidebarPeeking(true)}
|
onMouseEnter={() => !isSidebarLocked() && setIsSidebarPeeking(true)}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
@@ -158,8 +192,8 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={cn("flex-1 overflow-hidden transition-all duration-200", (!isSidebarLocked() && !isSidebarPeeking() && !(store.isNotepadMode && !selectedNoteId())) ? "opacity-0 pointer-events-none scale-95" : "opacity-100 scale-100")}>
|
<div class={cn("flex-1 overflow-hidden transition-all duration-200", (!isSidebarLocked() && !isSidebarPeeking() && !(store.isNotepadMode && !props.selectedNoteId)) ? "opacity-0 pointer-events-none scale-95" : "opacity-100 scale-100")}>
|
||||||
<NotesSidebar selectedNoteId={selectedNoteId()} setSelectedNoteId={setSelectedNoteId} />
|
<NotesSidebar selectedNoteId={props.selectedNoteId} setSelectedNoteId={setActiveNoteId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -208,7 +242,7 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
"flex-1 h-8 text-[0.7rem] font-bold uppercase tracking-widest z-10 transition-colors duration-200 flex items-center justify-center rounded-lg",
|
"flex-1 h-8 text-[0.7rem] font-bold uppercase tracking-widest z-10 transition-colors duration-200 flex items-center justify-center rounded-lg",
|
||||||
!store.isNotepadMode ? "text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
!store.isNotepadMode ? "text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
||||||
)}
|
)}
|
||||||
onClick={() => setStore('isNotepadMode', false)}
|
onClick={() => props.onOpenTasks()}
|
||||||
>
|
>
|
||||||
Tasks
|
Tasks
|
||||||
</button>
|
</button>
|
||||||
@@ -217,7 +251,7 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
"flex-1 h-8 text-[0.7rem] font-bold uppercase tracking-widest z-10 transition-colors duration-200 flex items-center justify-center rounded-lg",
|
"flex-1 h-8 text-[0.7rem] font-bold uppercase tracking-widest z-10 transition-colors duration-200 flex items-center justify-center rounded-lg",
|
||||||
store.isNotepadMode ? "text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
store.isNotepadMode ? "text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
||||||
)}
|
)}
|
||||||
onClick={() => setStore('isNotepadMode', true)}
|
onClick={() => props.onOpenNotes(props.selectedNoteId)}
|
||||||
>
|
>
|
||||||
Notes
|
Notes
|
||||||
</button>
|
</button>
|
||||||
@@ -253,7 +287,7 @@ export const Layout: Component<LayoutProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div class={cn(!store.isNotepadMode ? "hidden" : "block", "h-[calc(100vh-3.5rem)] md:h-[calc(100vh-8rem)] w-full")}>
|
<div class={cn(!store.isNotepadMode ? "hidden" : "block", "h-[calc(100vh-3.5rem)] md:h-[calc(100vh-8rem)] w-full")}>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<NotepadView selectedNoteId={selectedNoteId()} setSelectedNoteId={setSelectedNoteId} />
|
<NotepadView selectedNoteId={props.selectedNoteId} setSelectedNoteId={setActiveNoteId} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+14
-2
@@ -68,8 +68,20 @@ export const HelpView: Component<HelpViewProps> = (props) => {
|
|||||||
if (!id) return;
|
if (!id) return;
|
||||||
document.getElementById(`help-${id}`)?.scrollIntoView({ behavior, block: "start" });
|
document.getElementById(`help-${id}`)?.scrollIntoView({ behavior, block: "start" });
|
||||||
};
|
};
|
||||||
const getSectionFromHash = () => window.location.hash.startsWith("#help/") ? decodeURIComponent(window.location.hash.slice("#help/".length)) : undefined;
|
const getSectionFromHash = () => {
|
||||||
const jumpTo = (id: string) => { if (window.location.hash !== `#help/${id}`) window.location.hash = `#help/${id}`; scrollToSection(id); };
|
if (window.location.hash.startsWith("#/help/")) {
|
||||||
|
return decodeURIComponent(window.location.hash.slice("#/help/".length));
|
||||||
|
}
|
||||||
|
if (window.location.hash.startsWith("#help/")) {
|
||||||
|
return decodeURIComponent(window.location.hash.slice("#help/".length));
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
const jumpTo = (id: string) => {
|
||||||
|
const nextHash = `#/help/${encodeURIComponent(id)}`;
|
||||||
|
if (window.location.hash !== nextHash) window.location.hash = nextHash;
|
||||||
|
scrollToSection(id);
|
||||||
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const syncToHash = () => window.setTimeout(() => scrollToSection(getSectionFromHash(), "auto"), 50);
|
const syncToHash = () => window.setTimeout(() => scrollToSection(getSectionFromHash(), "auto"), 50);
|
||||||
|
|||||||
Reference in New Issue
Block a user