@monkey-mini-app/ui 0.1.0 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/globals.css +1 -1
- package/dist/index.js +12 -2
- package/dist/manifest.json +6 -6
- package/dist/runtime.js +9 -0
- package/dist/sdk.js +158 -0
- package/dist/src/blocks/activity-feed.tsx +7 -0
- package/dist/src/blocks/app-shell.tsx +1 -0
- package/dist/src/blocks/attachment-gallery.tsx +7 -0
- package/dist/src/blocks/bar-chart.tsx +15 -1
- package/dist/src/blocks/comment-thread.tsx +7 -0
- package/dist/src/blocks/dashboard-shell.tsx +109 -0
- package/dist/src/blocks/description-list.tsx +7 -0
- package/dist/src/blocks/detail-panel.tsx +1 -0
- package/dist/src/blocks/donut-chart.tsx +19 -2
- package/dist/src/blocks/env-badge.tsx +7 -0
- package/dist/src/blocks/env-table.tsx +316 -0
- package/dist/src/blocks/file-tree.tsx +7 -0
- package/dist/src/blocks/filter-bar.tsx +1 -0
- package/dist/src/blocks/form-sheet.tsx +160 -0
- package/dist/src/blocks/gauge.tsx +29 -6
- package/dist/src/blocks/list-detail.tsx +121 -0
- package/dist/src/blocks/notification-center.tsx +7 -0
- package/dist/src/blocks/page-header.tsx +1 -0
- package/dist/src/blocks/progress-ring.tsx +20 -2
- package/dist/src/blocks/radar-chart.tsx +18 -1
- package/dist/src/blocks/request-inspector.tsx +7 -0
- package/dist/src/blocks/reveal.tsx +56 -0
- package/dist/src/blocks/run-timeline.tsx +7 -0
- package/dist/src/blocks/scrollspy.tsx +7 -0
- package/dist/src/blocks/settings-split.tsx +154 -0
- package/dist/src/blocks/severity-chip.tsx +7 -0
- package/dist/src/blocks/sparkline.tsx +12 -3
- package/dist/src/blocks/stat-card.tsx +1 -0
- package/dist/src/blocks/status-badge.tsx +1 -0
- package/dist/src/blocks/table-page.tsx +83 -0
- package/dist/src/blocks/terminal.tsx +21 -3
- package/dist/src/blocks/test-step-list.tsx +7 -0
- package/dist/src/blocks/trend-card.tsx +7 -0
- package/dist/src/blocks/wizard-shell.tsx +132 -0
- package/dist/src/cdn-modules.d.ts +5 -0
- package/dist/src/components/accordion.tsx +7 -0
- package/dist/src/components/alert-dialog.tsx +7 -0
- package/dist/src/components/alert.tsx +7 -0
- package/dist/src/components/aspect-ratio.tsx +4 -0
- package/dist/src/components/attachment.tsx +4 -0
- package/dist/src/components/avatar.tsx +4 -0
- package/dist/src/components/badge.tsx +7 -0
- package/dist/src/components/breadcrumb.tsx +4 -0
- package/dist/src/components/bubble.tsx +4 -0
- package/dist/src/components/button-group.tsx +6 -0
- package/dist/src/components/button.tsx +7 -0
- package/dist/src/components/calendar.tsx +4 -0
- package/dist/src/components/card.tsx +8 -1
- package/dist/src/components/chart.tsx +7 -0
- package/dist/src/components/checkbox.tsx +5 -0
- package/dist/src/components/collapsible.tsx +4 -0
- package/dist/src/components/combobox.tsx +4 -0
- package/dist/src/components/command.tsx +6 -0
- package/dist/src/components/context-menu.tsx +4 -0
- package/dist/src/components/dialog.tsx +21 -0
- package/dist/src/components/drawer.tsx +4 -0
- package/dist/src/components/dropdown-menu.tsx +7 -0
- package/dist/src/components/empty.tsx +7 -0
- package/dist/src/components/field.tsx +6 -0
- package/dist/src/components/hover-card.tsx +4 -0
- package/dist/src/components/input-group.tsx +4 -0
- package/dist/src/components/input.tsx +7 -0
- package/dist/src/components/item.tsx +4 -0
- package/dist/src/components/kbd.tsx +4 -0
- package/dist/src/components/label.tsx +7 -0
- package/dist/src/components/marker.tsx +4 -0
- package/dist/src/components/menubar.tsx +4 -0
- package/dist/src/components/message-scroller.tsx +4 -0
- package/dist/src/components/message.tsx +4 -0
- package/dist/src/components/native-select.tsx +4 -0
- package/dist/src/components/navigation-menu.tsx +4 -0
- package/dist/src/components/pagination.tsx +4 -0
- package/dist/src/components/popover.tsx +7 -0
- package/dist/src/components/progress.tsx +7 -0
- package/dist/src/components/questionnaire.tsx +4 -0
- package/dist/src/components/radio-group.tsx +4 -0
- package/dist/src/components/resizable.tsx +7 -0
- package/dist/src/components/scroll-area.tsx +4 -0
- package/dist/src/components/select.tsx +7 -0
- package/dist/src/components/separator.tsx +5 -0
- package/dist/src/components/sheet.tsx +22 -0
- package/dist/src/components/sidebar.tsx +4 -0
- package/dist/src/components/skeleton.tsx +7 -0
- package/dist/src/components/slider.tsx +4 -0
- package/dist/src/components/spinner.tsx +6 -0
- package/dist/src/components/switch.tsx +7 -0
- package/dist/src/components/table.tsx +7 -0
- package/dist/src/components/tabs.tsx +7 -0
- package/dist/src/components/textarea.tsx +7 -0
- package/dist/src/components/toast.tsx +7 -0
- package/dist/src/components/toggle-group.tsx +4 -0
- package/dist/src/components/toggle.tsx +4 -0
- package/dist/src/components/tooltip.tsx +7 -0
- package/dist/src/composites/autocomplete.tsx +7 -0
- package/dist/src/composites/cascader.tsx +7 -0
- package/dist/src/composites/color-picker.tsx +7 -0
- package/dist/src/composites/confirm-dialog.tsx +7 -0
- package/dist/src/composites/copyable.tsx +9 -1
- package/dist/src/composites/currency-input.tsx +7 -0
- package/dist/src/composites/date-picker.tsx +7 -0
- package/dist/src/composites/date-range-picker.tsx +4 -0
- package/dist/src/composites/date-time-picker.tsx +7 -0
- package/dist/src/composites/date-time-range-picker.tsx +7 -0
- package/dist/src/composites/duration-input.tsx +7 -0
- package/dist/src/composites/mini-calendar.tsx +7 -0
- package/dist/src/composites/number-field.tsx +7 -0
- package/dist/src/composites/password-field.tsx +7 -0
- package/dist/src/composites/phone-input.tsx +7 -0
- package/dist/src/composites/rating.tsx +7 -0
- package/dist/src/composites/relative-date-picker.tsx +7 -0
- package/dist/src/composites/search-input.tsx +7 -0
- package/dist/src/composites/slider-range.tsx +7 -0
- package/dist/src/composites/tag-input.tsx +7 -0
- package/dist/src/composites/time-picker.tsx +11 -1
- package/dist/src/composites/time-range-picker.tsx +7 -0
- package/dist/src/composites/timezone-select.tsx +7 -0
- package/dist/src/composites/transfer.tsx +7 -0
- package/dist/src/composites/user-picker.tsx +7 -0
- package/dist/src/hooks/use-html-dark.ts +37 -0
- package/dist/src/hooks/use-mobile.ts +3 -0
- package/dist/src/i18n/en.ts +109 -2
- package/dist/src/i18n/zh.ts +99 -2
- package/dist/src/index.ts +147 -134
- package/dist/src/lib/clipboard.ts +10 -0
- package/dist/src/lib/illustrations.tsx +2 -2
- package/dist/src/products/code-block.tsx +35 -18
- package/dist/src/products/code-editor.tsx +156 -24
- package/dist/src/products/commit-graph.tsx +644 -0
- package/dist/src/products/data-grid.tsx +2 -1
- package/dist/src/products/diff-viewer.tsx +104 -8
- package/dist/src/products/event-calendar.tsx +28 -823
- package/dist/src/products/file-dropzone.tsx +7 -0
- package/dist/src/products/full-calendar/animations.ts +38 -0
- package/dist/src/products/full-calendar/calendar-body.tsx +77 -0
- package/dist/src/products/full-calendar/constants.ts +10 -0
- package/dist/src/products/full-calendar/contexts/calendar-context.tsx +255 -0
- package/dist/src/products/full-calendar/contexts/dnd-context.tsx +157 -0
- package/dist/src/products/full-calendar/dialogs/add-edit-event-dialog.tsx +187 -0
- package/dist/src/products/full-calendar/dialogs/delete-event-dialog.tsx +39 -0
- package/dist/src/products/full-calendar/dialogs/event-details-dialog.tsx +100 -0
- package/dist/src/products/full-calendar/dialogs/events-list-dialog.tsx +87 -0
- package/dist/src/products/full-calendar/dnd/draggable-event.tsx +45 -0
- package/dist/src/products/full-calendar/dnd/droppable-area.tsx +45 -0
- package/dist/src/products/full-calendar/dnd/resizable-event.tsx +179 -0
- package/dist/src/products/full-calendar/header/calendar-header.tsx +65 -0
- package/dist/src/products/full-calendar/header/date-navigator.tsx +110 -0
- package/dist/src/products/full-calendar/header/filter.tsx +58 -0
- package/dist/src/products/full-calendar/header/today-button.tsx +45 -0
- package/dist/src/products/full-calendar/header/user-select.tsx +64 -0
- package/dist/src/products/full-calendar/header/view-tabs.tsx +36 -0
- package/dist/src/products/full-calendar/helpers.ts +453 -0
- package/dist/src/products/full-calendar/hooks/use-time-range-create.ts +152 -0
- package/dist/src/products/full-calendar/hooks.ts +70 -0
- package/dist/src/products/full-calendar/interfaces.ts +23 -0
- package/dist/src/products/full-calendar/settings/settings.tsx +80 -0
- package/dist/src/products/full-calendar/shims/avatar-group.tsx +16 -0
- package/dist/src/products/full-calendar/shims/day-picker.tsx +1 -0
- package/dist/src/products/full-calendar/shims/motion.tsx +97 -0
- package/dist/src/products/full-calendar/shims/next-themes.ts +6 -0
- package/dist/src/products/full-calendar/shims/re-resizable.tsx +25 -0
- package/dist/src/products/full-calendar/shims/sonner.ts +4 -0
- package/dist/src/products/full-calendar/types.ts +8 -0
- package/dist/src/products/full-calendar/views/agenda-view/agenda-events.tsx +140 -0
- package/dist/src/products/full-calendar/views/month-view/calendar-month-view.tsx +69 -0
- package/dist/src/products/full-calendar/views/month-view/day-cell.tsx +201 -0
- package/dist/src/products/full-calendar/views/month-view/event-bullet.tsx +40 -0
- package/dist/src/products/full-calendar/views/month-view/month-event-badge.tsx +143 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-day-view.tsx +295 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-time-line.tsx +35 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/calendar-week-view.tsx +253 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/day-view-multi-day-events-row.tsx +73 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/event-block.tsx +114 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/render-grouped-events.tsx +49 -0
- package/dist/src/products/full-calendar/views/week-and-day-view/week-view-multi-day-events-row.tsx +143 -0
- package/dist/src/products/full-calendar/views/year-view/calendar-year-view.tsx +161 -0
- package/dist/src/products/gantt.tsx +7 -0
- package/dist/src/products/jira-wiki.tsx +1 -0
- package/dist/src/products/jql-input.tsx +340 -37
- package/dist/src/products/json-viewer.tsx +7 -0
- package/dist/src/products/kanban-issue-panel.tsx +7 -0
- package/dist/src/products/kanban.tsx +1 -0
- package/dist/src/products/log-viewer.tsx +390 -18
- package/dist/src/products/markdown-editor.tsx +21 -6
- package/dist/src/products/markdown.tsx +7 -0
- package/dist/src/products/rich-text-editor.tsx +65 -59
- package/dist/src/products/sortable-list.tsx +7 -0
- package/dist/src/products/stepper.tsx +219 -70
- package/dist/src/products/timeline.tsx +7 -0
- package/dist/src/products/tree-view.tsx +7 -0
- package/dist/src/sdk/app-error-boundary.tsx +240 -0
- package/dist/src/sdk/app-id.ts +7 -0
- package/dist/src/sdk/use-app.ts +126 -0
- package/dist/src/styles/globals.css +7 -7
- package/dist/vendors/lodash.js +20 -0
- package/dist/vendors/motion.js +12 -0
- package/package.json +23 -19
- package/dist/src/products/jql-language.ts +0 -70
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
|
-
import { Line, LineChart } from "recharts"
|
|
3
|
+
import { Line, LineChart, YAxis } from "recharts"
|
|
4
4
|
|
|
5
5
|
import { ChartContainer, type ChartConfig } from "@monkey-mini-app/ui/components/chart"
|
|
6
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
6
7
|
|
|
7
8
|
const config = {
|
|
8
9
|
value: { label: "Value", color: "var(--chart-1)" },
|
|
9
10
|
} satisfies ChartConfig
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Tiny inline trend line, no axes.
|
|
14
|
+
* @when Trend inside a table row or KPI card.
|
|
15
|
+
* @example
|
|
16
|
+
* <Sparkline data={[{ value: 3 }, { value: 9 }, { value: 6 }]} />
|
|
17
|
+
* @family Chart & data
|
|
18
|
+
*/
|
|
19
|
+
export function Sparkline({ data, className }: { data: { value: number }[]; className?: string }) {
|
|
12
20
|
return (
|
|
13
|
-
<ChartContainer config={config} className="h-
|
|
21
|
+
<ChartContainer config={config} className={cn("h-20 w-full", className)} data-testid="sparkline">
|
|
14
22
|
<LineChart data={data}>
|
|
23
|
+
<YAxis hide domain={["dataMin", "dataMax"]} />
|
|
15
24
|
<Line dataKey="value" type="monotone" stroke="var(--color-value)" strokeWidth={1.5} dot={false} />
|
|
16
25
|
</LineChart>
|
|
17
26
|
</ChartContainer>
|
|
@@ -16,6 +16,7 @@ export type StatCardProps = {
|
|
|
16
16
|
* @when Dashboard metric strip
|
|
17
17
|
* @example
|
|
18
18
|
* <StatCard title="Pass rate" value="98%" delta="+1.2%" trend="up" />
|
|
19
|
+
* @family Chart & data
|
|
19
20
|
*/
|
|
20
21
|
export function StatCard({ title, value, delta, trend = "flat", children }: StatCardProps) {
|
|
21
22
|
return (
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils";
|
|
6
|
+
|
|
7
|
+
export type TablePageProps = {
|
|
8
|
+
/** Page-level actions above the table: search, filters, "New". Pinned; it never scrolls away. */
|
|
9
|
+
toolbar?: ReactNode;
|
|
10
|
+
/** The table itself (a `DataGrid`, usually). The only slot that scrolls. */
|
|
11
|
+
grid?: ReactNode;
|
|
12
|
+
/** First-run / no-match placeholder. Renders when `grid` is not given. */
|
|
13
|
+
empty?: ReactNode;
|
|
14
|
+
/** A pager you own. `DataGrid` already renders its own when `features.pagination` is on. */
|
|
15
|
+
pagination?: ReactNode;
|
|
16
|
+
/** Bulk actions. Floats over the bottom of the grid instead of pushing the rows around. */
|
|
17
|
+
bulk?: ReactNode;
|
|
18
|
+
className?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Full-height table page: toolbar pinned, one scrolling band for the grid, bulk bar floating.
|
|
23
|
+
*
|
|
24
|
+
* Without it a `DataGrid` is dropped into a plain flex column, the tallest child wins, and the
|
|
25
|
+
* page scrolls instead of the table: the toolbar slides off, pagination ends up below the fold,
|
|
26
|
+
* and selecting 200 rows shoves the table down every time the bulk bar appears. The band owns
|
|
27
|
+
* `min-h-0` + `overflow-y-auto` here, and the bulk bar is out of flow with `pointer-events-none`
|
|
28
|
+
* on its track so the rows under it stay clickable.
|
|
29
|
+
*
|
|
30
|
+
* @when Page shape: one wide filterable table is the product — inventories, ledgers, saved views
|
|
31
|
+
* @example
|
|
32
|
+
* <TablePage
|
|
33
|
+
* toolbar={<FilterBar {...{ query, onQueryChange, filters }} />}
|
|
34
|
+
* grid={rows.length ? <DataGrid columns={columns} data={rows} /> : undefined}
|
|
35
|
+
* empty={<FirstRun onImport={import} />}
|
|
36
|
+
* bulk={selected.size ? <BulkBar count={selected.size} onClear={clear} /> : undefined}
|
|
37
|
+
* />
|
|
38
|
+
* @family Layout & structure
|
|
39
|
+
*/
|
|
40
|
+
export function TablePage({
|
|
41
|
+
toolbar,
|
|
42
|
+
grid,
|
|
43
|
+
empty,
|
|
44
|
+
pagination,
|
|
45
|
+
bulk,
|
|
46
|
+
className,
|
|
47
|
+
}: TablePageProps) {
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
className={cn("relative flex h-full min-h-0 w-full flex-col", className)}
|
|
51
|
+
data-testid="table-page"
|
|
52
|
+
>
|
|
53
|
+
{toolbar ? (
|
|
54
|
+
<div className="shrink-0" data-testid="table-page-toolbar">
|
|
55
|
+
{toolbar}
|
|
56
|
+
</div>
|
|
57
|
+
) : null}
|
|
58
|
+
|
|
59
|
+
{/* The one scroller. `grid ?? empty` keeps "no rows" the app's decision, not a prop. */}
|
|
60
|
+
<div
|
|
61
|
+
className="min-h-0 flex-1 overflow-y-auto"
|
|
62
|
+
data-testid="table-page-grid"
|
|
63
|
+
>
|
|
64
|
+
{grid ?? empty ?? null}
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{pagination ? (
|
|
68
|
+
<div className="shrink-0" data-testid="table-page-pagination">
|
|
69
|
+
{pagination}
|
|
70
|
+
</div>
|
|
71
|
+
) : null}
|
|
72
|
+
|
|
73
|
+
{bulk ? (
|
|
74
|
+
<div
|
|
75
|
+
className="pointer-events-none absolute inset-x-0 bottom-0 flex justify-center px-4 pb-4"
|
|
76
|
+
data-testid="table-page-bulk"
|
|
77
|
+
>
|
|
78
|
+
<div className="pointer-events-auto">{bulk}</div>
|
|
79
|
+
</div>
|
|
80
|
+
) : null}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -1,10 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fixed-width console block.
|
|
5
|
+
* @when Showing a command + its output verbatim. Timestamped levels/scrolling → `LogViewer`.
|
|
6
|
+
* @example
|
|
7
|
+
* <Terminal lines={["$ df -h", "/dev/disk1 62%"]} />
|
|
8
|
+
* @family Realtime
|
|
9
|
+
*/
|
|
10
|
+
export function Terminal({
|
|
11
|
+
lines,
|
|
12
|
+
className,
|
|
13
|
+
}: {
|
|
14
|
+
lines: string[];
|
|
15
|
+
className?: string;
|
|
16
|
+
}) {
|
|
2
17
|
return (
|
|
3
18
|
<pre
|
|
4
19
|
data-testid="terminal"
|
|
5
|
-
className=
|
|
20
|
+
className={cn(
|
|
21
|
+
"overflow-auto rounded-xl bg-zinc-950 p-3 font-mono text-xs leading-6 text-zinc-100",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
6
24
|
>
|
|
7
25
|
{lines.join("\n")}
|
|
8
26
|
</pre>
|
|
9
|
-
)
|
|
27
|
+
);
|
|
10
28
|
}
|
|
@@ -2,6 +2,13 @@ import { StatusBadge, type StatusKey } from "@monkey-mini-app/ui/blocks/status-b
|
|
|
2
2
|
|
|
3
3
|
export type TestStep = { id: string; title: string; status: StatusKey | string }
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Numbered test steps with expected/actual + status.
|
|
7
|
+
* @when QA cases, checklists that get executed and graded.
|
|
8
|
+
* @example
|
|
9
|
+
* <TestStepList steps={[{ id: "s1", name: "登录", status: "pass" }]} />
|
|
10
|
+
* @family Data & tables
|
|
11
|
+
*/
|
|
5
12
|
export function TestStepList({ steps }: { steps: TestStep[] }) {
|
|
6
13
|
return (
|
|
7
14
|
<ol className="flex flex-col gap-2" data-testid="test-step-list">
|
|
@@ -17,6 +17,13 @@ const chartConfig = {
|
|
|
17
17
|
value: { label: "Value", color: "var(--chart-1)" },
|
|
18
18
|
} satisfies ChartConfig
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* KPI card with a trend series.
|
|
22
|
+
* @when Metric + direction over time. Metric only → `StatCard`.
|
|
23
|
+
* @example
|
|
24
|
+
* <TrendCard title="请求数" value="1.2k" trend="up" data={[{ value: 9 }, { value: 12 }]} />
|
|
25
|
+
* @family Chart & data
|
|
26
|
+
*/
|
|
20
27
|
export function TrendCard({
|
|
21
28
|
title,
|
|
22
29
|
value,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { Button } from "@monkey-mini-app/ui/components/button";
|
|
6
|
+
import { Stepper, StepperItem } from "@monkey-mini-app/ui/products/stepper";
|
|
7
|
+
import { useLabels } from "@monkey-mini-app/ui/i18n/context";
|
|
8
|
+
import { cn } from "@monkey-mini-app/ui/lib/utils";
|
|
9
|
+
|
|
10
|
+
export type WizardStep = {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string;
|
|
13
|
+
/** Pass `false` while the step's form is incomplete — Next disables itself. Omit to allow. */
|
|
14
|
+
valid?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type WizardShellProps = {
|
|
18
|
+
/** The steps, in order. The rail is generated from this, so it cannot disagree with the body. */
|
|
19
|
+
steps: WizardStep[];
|
|
20
|
+
/** Which step is showing. The app owns it — the preset never navigates on its own. */
|
|
21
|
+
index: number;
|
|
22
|
+
/** Current step's content. The only scrolling area. */
|
|
23
|
+
body: ReactNode;
|
|
24
|
+
/** Replaces the generated Back/Next/Finish row (a preview pane, a second action…). */
|
|
25
|
+
footer?: ReactNode;
|
|
26
|
+
onBack?: () => void;
|
|
27
|
+
onNext?: () => void;
|
|
28
|
+
/** Fires on the last step's primary action. Without it the last step still shows Next. */
|
|
29
|
+
onSubmit?: () => void;
|
|
30
|
+
/** Greys the primary action and swaps in the busy label. */
|
|
31
|
+
submitting?: boolean;
|
|
32
|
+
className?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Multi-step flow: step rail on top, scrolling body, action row that never moves.
|
|
37
|
+
*
|
|
38
|
+
* Hand-rolled, a wizard's footer rides along inside the scrolling column, so every step change
|
|
39
|
+
* jumps the buttons to a different height and the user chases them with the cursor; and Next is
|
|
40
|
+
* enabled into an invalid step because the validity check lives in the form and the button lives
|
|
41
|
+
* outside it. Here the footer is a `shrink-0` sibling of the scroller, and Next/Finish are
|
|
42
|
+
* disabled straight from `steps[index].valid`, which is the only place that can know.
|
|
43
|
+
*
|
|
44
|
+
* @when Page shape: create / import / onboarding, one step at a time with validation per step
|
|
45
|
+
* @example
|
|
46
|
+
* <WizardShell
|
|
47
|
+
* steps={[{ id: "pick", label: "Choose file", valid: Boolean(file) }, { id: "map", label: "Map columns", valid: mapped }]}
|
|
48
|
+
* index={step}
|
|
49
|
+
* body={<StepBody />}
|
|
50
|
+
* onBack={() => setStep((s) => s - 1)}
|
|
51
|
+
* onNext={() => setStep((s) => s + 1)}
|
|
52
|
+
* onSubmit={() => void run()}
|
|
53
|
+
* />
|
|
54
|
+
* @family Layout & structure
|
|
55
|
+
*/
|
|
56
|
+
export function WizardShell({
|
|
57
|
+
steps,
|
|
58
|
+
index,
|
|
59
|
+
body,
|
|
60
|
+
footer,
|
|
61
|
+
onBack,
|
|
62
|
+
onNext,
|
|
63
|
+
onSubmit,
|
|
64
|
+
submitting = false,
|
|
65
|
+
className,
|
|
66
|
+
}: WizardShellProps) {
|
|
67
|
+
const t = useLabels("wizardShell");
|
|
68
|
+
const current = steps[index];
|
|
69
|
+
const last = index >= steps.length - 1;
|
|
70
|
+
const canAdvance = current?.valid !== false;
|
|
71
|
+
const primary = last && onSubmit ? t.finish : t.next;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
className={cn("flex h-full min-h-0 w-full flex-col", className)}
|
|
76
|
+
data-testid="wizard-shell"
|
|
77
|
+
>
|
|
78
|
+
<div
|
|
79
|
+
className="border-border shrink-0 border-b px-4 py-3"
|
|
80
|
+
data-testid="wizard-rail"
|
|
81
|
+
>
|
|
82
|
+
<Stepper orientation="horizontal">
|
|
83
|
+
{steps.map((s, i) => (
|
|
84
|
+
<StepperItem
|
|
85
|
+
key={s.id}
|
|
86
|
+
step={i + 1}
|
|
87
|
+
title={s.label}
|
|
88
|
+
status={
|
|
89
|
+
i < index ? "completed" : i === index ? "active" : "default"
|
|
90
|
+
}
|
|
91
|
+
/>
|
|
92
|
+
))}
|
|
93
|
+
</Stepper>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div
|
|
97
|
+
className="min-h-0 flex-1 overflow-y-auto px-4 py-4"
|
|
98
|
+
data-testid="wizard-body"
|
|
99
|
+
>
|
|
100
|
+
{body}
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div
|
|
104
|
+
className="border-border bg-background flex shrink-0 items-center justify-between gap-3 border-t px-4 py-3"
|
|
105
|
+
data-testid="wizard-footer"
|
|
106
|
+
>
|
|
107
|
+
{footer ?? (
|
|
108
|
+
<>
|
|
109
|
+
<Button
|
|
110
|
+
variant="ghost"
|
|
111
|
+
onClick={onBack}
|
|
112
|
+
disabled={index === 0 || submitting}
|
|
113
|
+
data-testid="wizard-back"
|
|
114
|
+
>
|
|
115
|
+
{t.back}
|
|
116
|
+
</Button>
|
|
117
|
+
<span className="text-muted-foreground text-xs tabular-nums">
|
|
118
|
+
{t.stepOf(index + 1, steps.length)}
|
|
119
|
+
</span>
|
|
120
|
+
<Button
|
|
121
|
+
onClick={last && onSubmit ? onSubmit : onNext}
|
|
122
|
+
disabled={!canAdvance || submitting}
|
|
123
|
+
data-testid="wizard-next"
|
|
124
|
+
>
|
|
125
|
+
{submitting ? t.working : primary}
|
|
126
|
+
</Button>
|
|
127
|
+
</>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -3,6 +3,13 @@ import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
|
|
|
3
3
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
4
4
|
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Vertically expanding sections.
|
|
8
|
+
* @when Collapsible groups in settings/details.
|
|
9
|
+
* @example
|
|
10
|
+
* <Accordion type="single"><AccordionItem value="s1"><AccordionTrigger>高级</AccordionTrigger><AccordionContent>…</AccordionContent></AccordionItem></Accordion>
|
|
11
|
+
* @family Surface
|
|
12
|
+
*/
|
|
6
13
|
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
|
7
14
|
return (
|
|
8
15
|
<AccordionPrimitive.Root
|
|
@@ -4,6 +4,13 @@ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog
|
|
|
4
4
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
5
5
|
import { Button } from "@monkey-mini-app/ui/components/button"
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Blocking confirm modal (no click-outside dismiss).
|
|
9
|
+
* @when Deleting/irreversible actions that must be answered.
|
|
10
|
+
* @example
|
|
11
|
+
* <AlertDialog open={ask} onOpenChange={setAsk}><AlertDialogContent><AlertDialogTitle>确定删除?</AlertDialogTitle><AlertDialogAction onClick={del}>删除</AlertDialogAction></AlertDialogContent></AlertDialog>
|
|
12
|
+
* @family Surface
|
|
13
|
+
*/
|
|
7
14
|
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
|
|
8
15
|
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
9
16
|
}
|
|
@@ -19,6 +19,13 @@ const alertVariants = cva(
|
|
|
19
19
|
}
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Inline message block (info/warning/error).
|
|
24
|
+
* @when Explaining a failed state in place. Transient → `toast()`.
|
|
25
|
+
* @example
|
|
26
|
+
* <Alert variant="destructive"><AlertTitle>拉取失败</AlertTitle><AlertDescription>上游 502,已用缓存</AlertDescription></Alert>
|
|
27
|
+
* @family Feedback & status
|
|
28
|
+
*/
|
|
22
29
|
function Alert({
|
|
23
30
|
className,
|
|
24
31
|
variant,
|
|
@@ -3,6 +3,10 @@ import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
|
|
|
3
3
|
|
|
4
4
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @family Layout & structure
|
|
8
|
+
* @when User initials/photo next to a name in rows, comments, pickers.
|
|
9
|
+
*/
|
|
6
10
|
function Avatar({
|
|
7
11
|
className,
|
|
8
12
|
size = "default",
|
|
@@ -27,6 +27,13 @@ const badgeVariants = cva(
|
|
|
27
27
|
}
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Inline label chip.
|
|
32
|
+
* @when Static tags/counts. Status → `StatusBadge`, severity → `SeverityChip`.
|
|
33
|
+
* @example
|
|
34
|
+
* <Badge variant="secondary">草稿</Badge>
|
|
35
|
+
* @family Feedback & status
|
|
36
|
+
*/
|
|
30
37
|
function Badge({
|
|
31
38
|
className,
|
|
32
39
|
variant = "default",
|
|
@@ -5,6 +5,10 @@ import { useRender } from "@base-ui/react/use-render"
|
|
|
5
5
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
6
6
|
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @family Navigation
|
|
10
|
+
* @when Deep mini-app views; usually one level is enough — prefer `Tabs`/`AppShell` nav.
|
|
11
|
+
*/
|
|
8
12
|
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
9
13
|
return (
|
|
10
14
|
<nav
|
|
@@ -22,6 +22,12 @@ const buttonGroupVariants = cva(
|
|
|
22
22
|
}
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @family Form
|
|
27
|
+
* @when 2–5 mutually related actions (view switch, alignment). Tabs of content → `Tabs`.
|
|
28
|
+
* @example
|
|
29
|
+
* <ButtonGroup><Button variant="outline">List</Button><Button variant="outline">Board</Button></ButtonGroup>
|
|
30
|
+
*/
|
|
25
31
|
function ButtonGroup({
|
|
26
32
|
className,
|
|
27
33
|
orientation,
|
|
@@ -40,6 +40,13 @@ const buttonVariants = cva(
|
|
|
40
40
|
}
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Action button with variants and icon sizing.
|
|
45
|
+
* @when Every click target. `variant="destructive"` for delete, `size="icon*"` for icon-only (add aria-label).
|
|
46
|
+
* @example
|
|
47
|
+
* <Button variant="outline" size="sm" onClick={run}>刷新</Button>
|
|
48
|
+
* @family Form
|
|
49
|
+
*/
|
|
43
50
|
function Button({
|
|
44
51
|
className,
|
|
45
52
|
variant = "default",
|
|
@@ -12,6 +12,10 @@ import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
|
12
12
|
import { Button, buttonVariants } from "@monkey-mini-app/ui/components/button"
|
|
13
13
|
import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from "lucide-react"
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @family Calendar & date
|
|
17
|
+
* @when An inline calendar inside your own layout. As a field with input → `DatePicker`.
|
|
18
|
+
*/
|
|
15
19
|
function Calendar({
|
|
16
20
|
className,
|
|
17
21
|
classNames,
|
|
@@ -2,6 +2,13 @@ import * as React from "react"
|
|
|
2
2
|
|
|
3
3
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Surface with header/content/footer.
|
|
7
|
+
* @when The default container for a block of related UI.
|
|
8
|
+
* @example
|
|
9
|
+
* <Card><CardHeader><CardTitle>今日摘要</CardTitle></CardHeader><CardContent>…</CardContent></Card>
|
|
10
|
+
* @family Surface
|
|
11
|
+
*/
|
|
5
12
|
function Card({
|
|
6
13
|
className,
|
|
7
14
|
size = "default",
|
|
@@ -12,7 +19,7 @@ function Card({
|
|
|
12
19
|
data-slot="card"
|
|
13
20
|
data-size={size}
|
|
14
21
|
className={cn(
|
|
15
|
-
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground
|
|
22
|
+
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl border border-border bg-card py-(--card-spacing) text-sm text-card-foreground [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
16
23
|
className
|
|
17
24
|
)}
|
|
18
25
|
{...props}
|
|
@@ -37,6 +37,13 @@ function useChart() {
|
|
|
37
37
|
return context
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Recharts container + theme CSS vars + tooltip/legend parts.
|
|
42
|
+
* @when Only when no `*Chart` block fits — then you own the series. Prefer `DonutChart` / `StackedBarChart` / `Sparkline` / `Gauge` / `RadarChart`.
|
|
43
|
+
* @example
|
|
44
|
+
* <ChartContainer config={cfg} className="h-40 w-full"><Bar data={rows} dataKey="v" /></ChartContainer>
|
|
45
|
+
* @family Chart & data
|
|
46
|
+
*/
|
|
40
47
|
function ChartContainer({
|
|
41
48
|
id,
|
|
42
49
|
className,
|
|
@@ -5,6 +5,11 @@ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
|
|
|
5
5
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
6
6
|
import { CheckIcon } from "lucide-react"
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Single boolean box.
|
|
10
|
+
* @when Multi-select rows / opt-in.
|
|
11
|
+
* @family Form
|
|
12
|
+
*/
|
|
8
13
|
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
|
9
14
|
return (
|
|
10
15
|
<CheckboxPrimitive.Root
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @family Layout & structure
|
|
5
|
+
* @when A single disclosure (advanced options). Several stacked → `Accordion`.
|
|
6
|
+
*/
|
|
3
7
|
function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
|
|
4
8
|
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
|
5
9
|
}
|
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
} from "@monkey-mini-app/ui/components/input-group"
|
|
12
12
|
import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @family Form
|
|
16
|
+
* @when When you need full control of the popover list. Simple suggestion box → `Autocomplete`.
|
|
17
|
+
*/
|
|
14
18
|
const Combobox = ComboboxPrimitive.Root
|
|
15
19
|
|
|
16
20
|
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
|
|
@@ -17,6 +17,12 @@ import {
|
|
|
17
17
|
} from "@monkey-mini-app/ui/components/input-group"
|
|
18
18
|
import { SearchIcon, CheckIcon } from "lucide-react"
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @family Navigation
|
|
22
|
+
* @when Jump between views/actions inside one app.
|
|
23
|
+
* @example
|
|
24
|
+
* <CommandDialog open={open} onOpenChange={setOpen}><CommandInput placeholder="跳转…" /><CommandList><CommandItem onSelect={go}>今日</CommandItem></CommandList></CommandDialog>
|
|
25
|
+
*/
|
|
20
26
|
function Command({
|
|
21
27
|
className,
|
|
22
28
|
...props
|
|
@@ -6,6 +6,10 @@ import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu
|
|
|
6
6
|
import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
7
7
|
import { ChevronRightIcon, CheckIcon } from "lucide-react"
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @family Navigation
|
|
11
|
+
* @when Row/canvas operations. Trigger-button actions → `DropdownMenu`.
|
|
12
|
+
*/
|
|
9
13
|
function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
|
|
10
14
|
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
|
11
15
|
}
|
|
@@ -5,6 +5,13 @@ import { cn } from "@monkey-mini-app/ui/lib/utils"
|
|
|
5
5
|
import { Button } from "@monkey-mini-app/ui/components/button"
|
|
6
6
|
import { XIcon } from "lucide-react"
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Modal overlay: Trigger + Content(+Header/Footer).
|
|
10
|
+
* @when Focused edit/confirm flow. Needs no focus takeover → `Sheet`. Destructive yes/no → `ConfirmDialog`. Size it with `width` on `DialogContent`: the base caps at `sm:max-w-sm`, so a `max-w-*` in `className` loses at ≥sm.
|
|
11
|
+
* @example
|
|
12
|
+
* <Dialog open={open} onOpenChange={setOpen}><DialogTrigger asChild><Button>编辑</Button></DialogTrigger><DialogContent><DialogHeader><DialogTitle>标题</DialogTitle></DialogHeader>…<DialogFooter><Button onClick={save}>保存</Button></DialogFooter></DialogContent></Dialog>
|
|
13
|
+
* @family Surface
|
|
14
|
+
*/
|
|
8
15
|
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
|
|
9
16
|
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
10
17
|
}
|
|
@@ -41,15 +48,29 @@ function DialogContent({
|
|
|
41
48
|
className,
|
|
42
49
|
children,
|
|
43
50
|
showCloseButton = true,
|
|
51
|
+
width,
|
|
52
|
+
style,
|
|
44
53
|
...props
|
|
45
54
|
}: DialogPrimitive.Popup.Props & {
|
|
46
55
|
showCloseButton?: boolean
|
|
56
|
+
/**
|
|
57
|
+
* Dialog width — a number is px. Inline `maxWidth`, because the base class caps the dialog at
|
|
58
|
+
* `sm:max-w-sm` and that utility outranks one the caller passes in `className`.
|
|
59
|
+
*/
|
|
60
|
+
width?: string | number
|
|
47
61
|
}) {
|
|
62
|
+
const maxWidth =
|
|
63
|
+
width == null
|
|
64
|
+
? undefined
|
|
65
|
+
: typeof width === "number"
|
|
66
|
+
? `${width}px`
|
|
67
|
+
: width
|
|
48
68
|
return (
|
|
49
69
|
<DialogPortal>
|
|
50
70
|
<DialogOverlay />
|
|
51
71
|
<DialogPrimitive.Popup
|
|
52
72
|
data-slot="dialog-content"
|
|
73
|
+
style={maxWidth == null ? style : { ...style, maxWidth }}
|
|
53
74
|
className={cn(
|
|
54
75
|
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
55
76
|
className
|