@ 关键路径/ svelte
Svelte 5 Runes 关键路径的活性集成.
升级? 升级一次
@critical-path/*组合在一起。 编码代理: 遵循 更新(md); 已标记断开的更改 刹车 此软件包中CHANGELOG.md。 。 。 .
@critical-path/svelte 提供Svelte 5 Runes反应性国家班级和工厂(ProjectState, (中文(简体) ), TaskState, (中文(简体) ), KanbanState, (中文(简体) ), TaskTransitionsState, (中文(简体) ), WorkflowState, (中文(简体) ), CommentState, (中文(简体) ), AttachmentState, (中文(简体) ), TaskActivityState, (中文(简体) ), DeliverableState, (中文(简体) ), DeliverableSummaryState, (中文(简体) ), WebMcpState在Svelte 5和SvelteKit应用中用于建设项目管理 UIs.
* 安装
npm install @critical-path/svelte svelte@^5.0.0
# or
pnpm add @critical-path/svelte svelte@^5.0.0
使用实例( Svelte 5 Runes)
1. 项目和任务
<script lang="ts">
import { onMount } from 'svelte';
import {
createCriticalPathClient,
createProjectState,
createTaskState
} from '@critical-path/svelte';
const client = createCriticalPathClient({ baseUrl: '/api/critical-path' });
const projectState = createProjectState(client);
const taskState = createTaskState(client, 'proj_1');
onMount(() => {
projectState.fetch();
taskState.fetch();
});
</script>
{#if taskState.loading || projectState.loading}
<p>Loading tasks...</p>
{:else if taskState.error}
<p style="color: red;">Error: {taskState.error.message}</p>
{:else}
<ul>
{#each taskState.data as task}
<li><strong>{task.title}</strong> - {task.status}</li>
{/each}
</ul>
{/if}
2. 反动Kanban委员会(KanbanState(中文(简体) )
Buckets 任务被动进入工作流程列(backlog, (中文(简体) ), todo, (中文(简体) ), in_progress等)或语义列(not_started, (中文(简体) ), in_progress, (中文(简体) ), completed, (中文(简体) ), canceled:
<script lang="ts">
import { onMount } from 'svelte';
import { createCriticalPathClient, createKanbanState } from '@critical-path/svelte';
const client = createCriticalPathClient({ baseUrl: '/api/critical-path' });
const kanban = createKanbanState(client, 'proj_1');
onMount(() => {
kanban.fetch();
});
</script>
<div class="board" style="display: flex; gap: 16px;">
{#each Object.entries(kanban.columns) as [status, tasks]}
<div class="column">
<h3>{status} ({tasks.length})</h3>
{#each tasks as task}
<div class="card">
<h4>{task.title}</h4>
<button on:click={() => kanban.moveTask(task.id, 'done')}>Mark Done</button>
</div>
{/each}
</div>
{/each}
</div>
3. 统一任务活动和线索讨论(TaskActivityState(中文(简体) )
将线性注释与内置附件合并( Nameattachment.commentId === comment.id单活性商店的独立附件:
<script lang="ts">
import { onMount } from 'svelte';
import { createCriticalPathClient, createTaskActivityState } from '@critical-path/svelte';
const client = createCriticalPathClient({ baseUrl: '/api/critical-path' });
const activityState = createTaskActivityState(client, 'task_1');
onMount(() => {
activityState.fetch();
});
async function handleSend(text: string, fileUrl?: string) {
await activityState.addComment(
{ content: text },
fileUrl ? [{ filename: 'upload.png', url: fileUrl, mimeType: 'image/png', sizeBytes: 1024 }] : []
);
}
</script>
{#each activityState.threads as thread}
<div class="comment">
<p><strong>{thread.authorId}</strong>: {thread.content}</p>
<!-- Inline comment attachments -->
{#if thread.attachments.length > 0}
<div class="attachments">
{#each thread.attachments as att}
<a href={att.url} target="_blank">{att.filename}</a>
{/each}
</div>
{/if}
<!-- Emoji Reactions -->
<div class="reactions">
<button on:click={() => activityState.addReaction(thread.id, '👍')}>👍</button>
<button on:click={() => activityState.addReaction(thread.id, '🚀')}>🚀</button>
<span>{thread.reactions?.length || 0} reactions</span>
</div>
<!-- Threaded replies -->
{#each thread.replies as reply}
<div class="reply">
<p>↪ {reply.authorId}: {reply.content}</p>
</div>
{/each}
</div>
{/each}
4. 抽象梯子TimelineLadderState(中文(简体) )
在宏观阶段健康、标准甘特任务与CPM关键路径以及使用Svelte 5 Runes的可实现/努力之间流畅地进行:
<script lang="ts">
import { onMount } from 'svelte';
import { createCriticalPathClient, createTimelineLadderState, createCriticalPathState } from '@critical-path/svelte';
const client = createCriticalPathClient({ baseUrl: '/api/critical-path' });
const ladderState = createTimelineLadderState(client, 'proj_1', { level: 'all' });
const cpmState = createCriticalPathState(client, 'proj_1');
onMount(() => {
ladderState.fetch();
cpmState.fetch();
});
</script>
<!-- Level Switcher -->
<div class="flex gap-2">
<button on:click={() => ladderState.setLevel('macro')}>Macro</button>
<button on:click={() => ladderState.setLevel('standard')}>Standard Gantt</button>
<button on:click={() => ladderState.setLevel('concrete')}>Concrete</button>
<button on:click={() => ladderState.setLevel('all')}>All Rungs</button>
</div>
{#if ladderState.loading}
<p>Loading timeline ladder...</p>
{:else}
<!-- Macro Rung -->
{#if ladderState.macro}
<div class="macro-banner">
<h3>Phase Health: {ladderState.macro.health} ({ladderState.macro.overallProgressPercentage}% Complete)</h3>
<p>Total Duration: {ladderState.macro.totalDurationHours}h | Critical Path: {ladderState.macro.criticalPathDurationHours}h</p>
</div>
{/if}
<!-- Standard Rung -->
{#if ladderState.standard}
<div class="standard-gantt">
<h4>Gantt Schedule</h4>
{#each ladderState.standard.tasks as item}
<div class:is-critical={item.isCritical}>
{item.task.title} (Early Start: {item.schedule.earlyStart}h, Total Slack: {item.schedule.totalSlack}h)
</div>
{/each}
</div>
{/if}
<!-- Concrete Rung -->
{#if ladderState.concrete}
<div class="concrete-grounding">
<h4>Concrete Ground Truth</h4>
{#each Object.entries(ladderState.concrete) as [taskId, evidence]}
<div>Task {taskId}: {evidence.deliverables.length} deliverables, {evidence.attachments.length} files</div>
{/each}
</div>
{/if}
{/if}
& & 清理
每个状态类在 fetch() (或 fetchMetrics(),.)被再次唤起,所以最新的呼叫总是获胜. 打电话 destroy() 当一个组件消失以取消任何仍在飞行中的东西时:
<script lang="ts">
import { onDestroy } from 'svelte';
const tasks = createTaskState(client, projectId);
onDestroy(() => tasks.destroy());
</script>
许可证
麻省理工学院 关键路径