@workspace-fs/react
Version:
React hooks for Workspace-FS - Multi-project file system management
513 lines (509 loc) • 15.9 kB
JavaScript
;
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;