UNPKG

@workspace-fs/react

Version:

React hooks for Workspace-FS - Multi-project file system management

513 lines (509 loc) 15.9 kB
'use strict'; var react = require('react'); var jsxRuntime = require('react/jsx-runtime'); var core = require('@workspace-fs/core'); // src/context/WorkspaceContext.tsx var WorkspaceContext = react.createContext(null); function WorkspaceProvider({ children, workspace }) { const value = { workspace }; return /* @__PURE__ */ jsxRuntime.jsx(WorkspaceContext.Provider, { value, children }); } function useWorkspaceContext() { const context = react.useContext(WorkspaceContext); if (!context) { throw new Error( "useWorkspaceContext must be used within a WorkspaceProvider" ); } return context; } var workspaceInstance = null; function getWorkspace() { if (!workspaceInstance) { workspaceInstance = new core.WorkspaceFileSystem(); } return workspaceInstance; } async function initializeWorkspace(providers = []) { const workspace = getWorkspace(); providers.forEach((provider) => { workspace.registerProvider(provider); }); await workspace.initialize(); return workspace; } function resetWorkspace() { workspaceInstance = null; } function useWorkspace() { const { workspace } = useWorkspaceContext(); const loadProject = react.useCallback( async (config) => { return workspace.loadProject(config); }, [workspace] ); const unloadProject = react.useCallback( async (projectId) => { return workspace.unloadProject(projectId); }, [workspace] ); const setActiveProject = react.useCallback( async (projectId) => { return workspace.setActiveProject(projectId); }, [workspace] ); const deleteProject = react.useCallback( async (projectId, options) => { return workspace.deleteProject(projectId, options); }, [workspace] ); const enableProject = react.useCallback( async (projectId) => { return workspace.enableProject(projectId); }, [workspace] ); const disableProject = react.useCallback( async (projectId) => { return workspace.disableProject(projectId); }, [workspace] ); const importFromUrl = react.useCallback( async (url) => { return workspace.importFromUrl(url); }, [workspace] ); const exportWorkspace = react.useCallback( async (options) => { return workspace.exportWorkspace(options); }, [workspace] ); const registerProvider = react.useCallback( (provider) => { workspace.registerProvider(provider); }, [workspace] ); return { // Workspace instance (for advanced use cases) workspace, // Project management loadProject, unloadProject, setActiveProject, deleteProject, enableProject, disableProject, // Import/Export importFromUrl, exportWorkspace, // Provider management registerProvider }; } function useProjects() { const { workspace } = useWorkspaceContext(); const [projects, setProjects] = react.useState([]); const [activeProjectId, setActiveProjectId] = react.useState(null); react.useEffect(() => { setProjects(workspace.getProjects()); const activeProject2 = workspace.getActiveProject(); setActiveProjectId(activeProject2?.id || null); const handleProjectLoaded = ({ project }) => { setProjects(workspace.getProjects()); }; const handleProjectUnloaded = ({ projectId }) => { setProjects(workspace.getProjects()); if (projectId === activeProjectId) { setActiveProjectId(null); } }; const handleProjectActivated = ({ projectId }) => { setActiveProjectId(projectId); }; const handleProjectDeleted = ({ projectId }) => { setProjects(workspace.getProjects()); if (projectId === activeProjectId) { setActiveProjectId(null); } }; const handleProjectEnabled = () => { setProjects(workspace.getProjects()); }; const handleProjectDisabled = () => { setProjects(workspace.getProjects()); }; const subscriptions = [ workspace.events.on("project:loaded", handleProjectLoaded), workspace.events.on("project:unloaded", handleProjectUnloaded), workspace.events.on("project:activated", handleProjectActivated), workspace.events.on("project:deleted", handleProjectDeleted), workspace.events.on("project:enabled", handleProjectEnabled), workspace.events.on("project:disabled", handleProjectDisabled) ]; return () => { subscriptions.forEach((disposable) => disposable.dispose()); }; }, [workspace, activeProjectId]); const activeProject = react.useMemo( () => projects.find((p) => p.id === activeProjectId) || null, [projects, activeProjectId] ); const getProjectsByType = react.useMemo( () => (type) => projects.filter((p) => p.source.type === type), [projects] ); const searchProjects = react.useMemo( () => (query) => { const lowerQuery = query.toLowerCase(); return projects.filter( (p) => p.name.toLowerCase().includes(lowerQuery) || p.id.toLowerCase().includes(lowerQuery) ); }, [projects] ); return { // All projects projects, // Active project activeProject, activeProjectId, // Computed values projectCount: projects.length, // Helper methods getProjectsByType, searchProjects }; } function useProject(projectId) { const { workspace } = useWorkspaceContext(); const [project, setProject] = react.useState(null); react.useEffect(() => { if (!projectId) { setProject(null); return; } const currentProject = workspace.getProject(projectId); setProject(currentProject); const handleProjectLoaded = ({ project: loadedProject }) => { if (loadedProject.id === projectId) { setProject(loadedProject); } }; const handleProjectUnloaded = ({ projectId: unloadedId }) => { if (unloadedId === projectId) { setProject(null); } }; const handleProjectDeleted = ({ projectId: deletedId }) => { if (deletedId === projectId) { setProject(null); } }; const handleProjectEnabled = ({ projectId: enabledId }) => { if (enabledId === projectId) { const enabledProject = workspace.getProject(projectId); setProject(enabledProject); } }; const handleProjectDisabled = ({ projectId: disabledId }) => { if (disabledId === projectId) { setProject(null); } }; const handleProjectError = ({ projectId: errorId, error }) => { if (errorId === projectId) { const errorProject = workspace.getProject(projectId); setProject(errorProject); } }; const subscriptions = [ workspace.events.on("project:loaded", handleProjectLoaded), workspace.events.on("project:unloaded", handleProjectUnloaded), workspace.events.on("project:deleted", handleProjectDeleted), workspace.events.on("project:enabled", handleProjectEnabled), workspace.events.on("project:disabled", handleProjectDisabled), workspace.events.on("project:error", handleProjectError) ]; return () => { subscriptions.forEach((disposable) => disposable.dispose()); }; }, [workspace, projectId]); return project; } function useActiveProject() { const { workspace } = useWorkspaceContext(); const [project, setProject] = react.useState(null); const [fs, setFs] = react.useState(void 0); react.useEffect(() => { const activeProject = workspace.getActiveProject(); setProject(activeProject); setFs(activeProject?.fs); const handleProjectActivated = ({ projectId }) => { const newActiveProject = workspace.getProject(projectId); setProject(newActiveProject); setFs(newActiveProject?.fs); }; const handleProjectUnloaded = ({ projectId }) => { const currentActive = workspace.getActiveProject(); if (!currentActive || currentActive.id === projectId) { setProject(null); setFs(void 0); } }; const handleProjectDeleted = ({ projectId }) => { const currentActive = workspace.getActiveProject(); if (!currentActive || currentActive.id === projectId) { setProject(null); setFs(void 0); } }; const handleProjectDisabled = ({ projectId }) => { const currentActive = workspace.getActiveProject(); if (!currentActive || currentActive.id === projectId) { setProject(null); setFs(void 0); } }; const subscriptions = [ workspace.events.on("project:activated", handleProjectActivated), workspace.events.on("project:deactivated", handleProjectUnloaded), workspace.events.on("project:unloaded", handleProjectUnloaded), workspace.events.on("project:deleted", handleProjectDeleted), workspace.events.on("project:disabled", handleProjectDisabled) ]; return () => { subscriptions.forEach((disposable) => disposable.dispose()); }; }, [workspace]); return { // The active project project, // Direct access to filesystem (convenience) fs, // Computed values projectId: project?.id, projectName: project?.name, projectType: project?.source.type, isLoaded: !!project }; } function useWorkspaceEvents(eventName, callback) { const { workspace } = useWorkspaceContext(); const callbackRef = react.useRef(callback); callbackRef.current = callback; react.useEffect(() => { const unsubscribe = workspace.events.on(eventName, (payload) => { callbackRef.current(payload); }); return () => { unsubscribe.dispose(); }; }, [workspace, eventName]); } function useProjectSelector(projectId, selector) { const { workspace } = useWorkspaceContext(); const getProject = () => { if (!projectId) return null; return workspace.getProject(projectId); }; const getSelectedValue = () => { const project = getProject(); return selector(project); }; const [selectedValue, setSelectedValue] = react.useState(getSelectedValue); const previousValueRef = react.useRef(selectedValue); const memoizedSelector = react.useMemo(() => selector, [selector]); react.useEffect(() => { if (!projectId) { const newValue = memoizedSelector(null); if (!Object.is(previousValueRef.current, newValue)) { previousValueRef.current = newValue; setSelectedValue(newValue); } return; } const checkAndUpdate = () => { const project = workspace.getProject(projectId); const newValue = memoizedSelector(project); if (!Object.is(previousValueRef.current, newValue)) { previousValueRef.current = newValue; setSelectedValue(newValue); } }; checkAndUpdate(); const handleProjectLoaded = ({ project: loadedProject }) => { if (loadedProject.id === projectId) { checkAndUpdate(); } }; const handleProjectUnloaded = ({ projectId: unloadedId }) => { if (unloadedId === projectId) { checkAndUpdate(); } }; const handleProjectDeleted = ({ projectId: deletedId }) => { if (deletedId === projectId) { checkAndUpdate(); } }; const handleProjectEnabled = ({ projectId: enabledId }) => { if (enabledId === projectId) { checkAndUpdate(); } }; const handleProjectDisabled = ({ projectId: disabledId }) => { if (disabledId === projectId) { checkAndUpdate(); } }; const handleProjectError = ({ projectId: errorId }) => { if (errorId === projectId) { checkAndUpdate(); } }; const subscriptions = [ workspace.events.on("project:loaded", handleProjectLoaded), workspace.events.on("project:unloaded", handleProjectUnloaded), workspace.events.on("project:deleted", handleProjectDeleted), workspace.events.on("project:enabled", handleProjectEnabled), workspace.events.on("project:disabled", handleProjectDisabled), workspace.events.on("project:error", handleProjectError) ]; return () => { subscriptions.forEach((disposable) => disposable.dispose()); }; }, [workspace, projectId, memoizedSelector]); return selectedValue; } function useProjectsSelector(selector) { const { workspace } = useWorkspaceContext(); const getProjects = () => { return workspace.getProjects(); }; const getSelectedValue = () => { const projects = getProjects(); return selector(projects); }; const [selectedValue, setSelectedValue] = react.useState(getSelectedValue); const previousValueRef = react.useRef(selectedValue); const memoizedSelector = react.useMemo(() => selector, [selector]); react.useEffect(() => { const checkAndUpdate = () => { const projects = workspace.getProjects(); const newValue = memoizedSelector(projects); if (!Object.is(previousValueRef.current, newValue)) { previousValueRef.current = newValue; setSelectedValue(newValue); } }; checkAndUpdate(); const events = [ "project:loaded", "project:unloaded", "project:deleted", "project:enabled", "project:disabled" ]; const subscriptions = events.map( (eventName) => workspace.events.on(eventName, checkAndUpdate) ); return () => { subscriptions.forEach((disposable) => disposable.dispose()); }; }, [workspace, memoizedSelector]); return selectedValue; } function useActiveProjectSelector(selector) { const { workspace } = useWorkspaceContext(); const getActiveProject = () => { return workspace.getActiveProject(); }; const getSelectedValue = () => { const project = getActiveProject(); return selector(project); }; const [selectedValue, setSelectedValue] = react.useState(getSelectedValue); const previousValueRef = react.useRef(selectedValue); const memoizedSelector = react.useMemo(() => selector, [selector]); react.useEffect(() => { const checkAndUpdate = () => { const project = workspace.getActiveProject(); const newValue = memoizedSelector(project); if (!Object.is(previousValueRef.current, newValue)) { previousValueRef.current = newValue; setSelectedValue(newValue); } }; checkAndUpdate(); const handleProjectActivated = () => checkAndUpdate(); const handleProjectChanged = ({ projectId }) => { const activeProject = workspace.getActiveProject(); if (activeProject?.id === projectId) { checkAndUpdate(); } }; const subscriptions = [ workspace.events.on("project:activated", handleProjectActivated), workspace.events.on("project:deactivated", handleProjectActivated), workspace.events.on("project:unloaded", handleProjectChanged), workspace.events.on("project:deleted", handleProjectChanged), workspace.events.on("project:disabled", handleProjectChanged), workspace.events.on("project:error", handleProjectChanged) ]; return () => { subscriptions.forEach((disposable) => disposable.dispose()); }; }, [workspace, memoizedSelector]); return selectedValue; } exports.WorkspaceProvider = WorkspaceProvider; exports.getWorkspace = getWorkspace; exports.initializeWorkspace = initializeWorkspace; exports.resetWorkspace = resetWorkspace; exports.useActiveProject = useActiveProject; exports.useActiveProjectSelector = useActiveProjectSelector; exports.useProject = useProject; exports.useProjectSelector = useProjectSelector; exports.useProjects = useProjects; exports.useProjectsSelector = useProjectsSelector; exports.useWorkspace = useWorkspace; exports.useWorkspaceContext = useWorkspaceContext; exports.useWorkspaceEvents = useWorkspaceEvents;