@almanaclabs/blaze-ui 0.3.2 → 0.5.0
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 +23 -0
- package/dist/components/accordion/accordion.js +53 -0
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/alert/alert.js +43 -0
- package/dist/components/alert-dialog/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog/alert-dialog.js +94 -0
- package/dist/components/aspect-ratio/aspect-ratio.js +13 -0
- package/dist/components/attachment/attachment.d.ts +2 -2
- package/dist/components/attachment/attachment.js +104 -0
- package/dist/components/avatar/avatar.d.ts +6 -4
- package/dist/components/avatar/avatar.d.ts.map +1 -1
- package/dist/components/avatar/avatar.js +51 -0
- package/dist/components/badge/badge.d.ts +3 -2
- package/dist/components/badge/badge.d.ts.map +1 -1
- package/dist/components/badge/badge.js +45 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +72 -0
- package/dist/components/bubble/bubble.d.ts +1 -1
- package/dist/components/bubble/bubble.js +73 -0
- package/dist/components/button/button.d.ts +5 -3
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +65 -0
- package/dist/components/button/index.d.ts +1 -1
- package/dist/components/button/index.d.ts.map +1 -1
- package/dist/components/button-group/button-group.js +41 -0
- package/dist/components/calendar/calendar.d.ts.map +1 -1
- package/dist/components/calendar/calendar.js +92 -0
- package/dist/components/card/card.js +56 -0
- package/dist/components/carousel/carousel.d.ts.map +1 -1
- package/dist/components/carousel/carousel.js +110 -0
- package/dist/components/chart/chart.js +131 -0
- package/dist/components/checkbox/checkbox.js +20 -0
- package/dist/components/collapsible/collapsible.js +23 -0
- package/dist/components/combobox/combobox.d.ts.map +1 -1
- package/dist/components/combobox/combobox.js +165 -0
- package/dist/components/data-table/data-table.js +30 -0
- package/dist/components/date-picker/date-picker.js +39 -0
- package/dist/components/dialog/dialog.js +92 -0
- package/dist/components/drawer/drawer.js +121 -0
- package/dist/components/dropdown-menu/dropdown-menu.js +136 -0
- package/dist/components/empty/empty.d.ts.map +1 -1
- package/dist/components/empty/empty.js +59 -0
- package/dist/components/field/field.d.ts.map +1 -1
- package/dist/components/field/field.js +107 -0
- package/dist/components/hover-card/hover-card.js +36 -0
- package/dist/components/identity-mark/identity-mark.d.ts +5 -0
- package/dist/components/identity-mark/identity-mark.d.ts.map +1 -0
- package/dist/components/identity-mark/identity-mark.js +20 -0
- package/dist/components/identity-mark/index.d.ts +2 -0
- package/dist/components/identity-mark/index.d.ts.map +1 -0
- package/dist/components/input/input.d.ts +9 -2
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +28 -0
- package/dist/components/input-group/input-group.d.ts +11 -3
- package/dist/components/input-group/input-group.d.ts.map +1 -1
- package/dist/components/input-group/input-group.js +95 -0
- package/dist/components/item/item.d.ts +2 -2
- package/dist/components/item/item.js +121 -0
- package/dist/components/label/label.js +13 -0
- package/dist/components/marker/marker.d.ts +1 -1
- package/dist/components/marker/marker.js +43 -0
- package/dist/components/message/message.js +49 -0
- package/dist/components/metric-overview/index.d.ts +2 -0
- package/dist/components/metric-overview/index.d.ts.map +1 -0
- package/dist/components/metric-overview/metric-overview.d.ts +12 -0
- package/dist/components/metric-overview/metric-overview.d.ts.map +1 -0
- package/dist/components/metric-overview/metric-overview.js +42 -0
- package/dist/components/native-select/native-select.d.ts +2 -1
- package/dist/components/native-select/native-select.d.ts.map +1 -1
- package/dist/components/native-select/native-select.js +40 -0
- package/dist/components/navigation-action/index.d.ts +2 -0
- package/dist/components/navigation-action/index.d.ts.map +1 -0
- package/dist/components/navigation-action/navigation-action.d.ts +6 -0
- package/dist/components/navigation-action/navigation-action.d.ts.map +1 -0
- package/dist/components/navigation-action/navigation-action.js +18 -0
- package/dist/components/pagination/pagination.d.ts.map +1 -1
- package/dist/components/pagination/pagination.js +80 -0
- package/dist/components/popover/popover.js +54 -0
- package/dist/components/progress/progress.js +45 -0
- package/dist/components/questionnaire/questionnaire-primitive.js +353 -0
- package/dist/components/questionnaire/questionnaire.d.ts.map +1 -1
- package/dist/components/questionnaire/questionnaire.js +169 -0
- package/dist/components/radio-group/radio-group.js +26 -0
- package/dist/components/scroll-area/scroll-area.js +37 -0
- package/dist/components/select/select.d.ts +2 -1
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +99 -0
- package/dist/components/separator/separator.js +15 -0
- package/dist/components/sheet/sheet.js +89 -0
- package/dist/components/sidebar/sidebar.d.ts +13 -2
- package/dist/components/sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar/sidebar.js +419 -0
- package/dist/components/skeleton/skeleton.js +12 -0
- package/dist/components/slider/slider.js +37 -0
- package/dist/components/spinner/spinner.js +15 -0
- package/dist/components/switch/switch.js +19 -0
- package/dist/components/table/table.d.ts +4 -1
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +68 -0
- package/dist/components/tabs/tabs.d.ts +1 -1
- package/dist/components/tabs/tabs.js +46 -0
- package/dist/components/textarea/textarea.js +13 -0
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +116 -0
- package/dist/components/toggle/toggle.d.ts +1 -1
- package/dist/components/toggle/toggle.js +35 -0
- package/dist/components/toggle-group/toggle-group.js +52 -0
- package/dist/components/tooltip/tooltip.js +41 -0
- package/dist/hooks/use-mobile.js +14 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +59 -3699
- package/dist/lib/control-size.d.ts +9 -0
- package/dist/lib/control-size.d.ts.map +1 -0
- package/dist/lib/control-size.js +10 -0
- package/dist/lib/utils.js +8 -0
- package/dist/recipes/recipe-runtime.d.ts +18 -0
- package/dist/recipes/recipe-runtime.d.ts.map +1 -0
- package/package.json +2 -1
- package/recipes/README.md +24 -0
- package/recipes/guided-workflow/README.md +51 -0
- package/recipes/guided-workflow/guided-workflow.tsx +495 -0
- package/recipes/manifest.json +19 -0
- package/recipes/workspace-shell/README.md +95 -0
- package/recipes/workspace-shell/assets/google-search-ads.svg +6 -0
- package/recipes/workspace-shell/assets/home.svg +5 -0
- package/recipes/workspace-shell/assets/radiant-health.png +0 -0
- package/recipes/workspace-shell/workspace-shell.css +55 -0
- package/recipes/workspace-shell/workspace-shell.tsx +636 -0
- package/src/styles/fonts/Sohne-Book.woff2 +0 -0
- package/src/styles/fonts/Sohne-Medium.woff2 +0 -0
- package/src/styles/fonts/Sohne-Semi.woff2 +0 -0
- package/src/styles/globals.css +233 -54
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const controlSizeClassNames: {
|
|
2
|
+
readonly xs: "h-6 rounded-[6px] px-1.5 text-[13px] leading-[14px] tracking-[0.01em] [--control-padding-inline:0.375rem]";
|
|
3
|
+
readonly sm: "h-7 rounded-[6px] px-2 text-sm leading-5 tracking-[0.01em] [--control-padding-inline:0.5rem]";
|
|
4
|
+
readonly md: "h-8 rounded-[8px] px-[11px] text-sm leading-[15px] tracking-[0.01em] [--control-padding-inline:11px]";
|
|
5
|
+
readonly lg: "h-9 rounded-[8px] px-[13px] text-base leading-[15px] [--control-padding-inline:13px]";
|
|
6
|
+
readonly xl: "h-12 rounded-[10px] px-[18px] text-base leading-5 [--control-padding-inline:18px]";
|
|
7
|
+
};
|
|
8
|
+
export type ControlSize = keyof typeof controlSizeClassNames;
|
|
9
|
+
//# sourceMappingURL=control-size.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-size.d.ts","sourceRoot":"","sources":["../../src/lib/control-size.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB;;;;;;CAMxB,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/lib/control-size.ts
|
|
2
|
+
var e = {
|
|
3
|
+
xs: "h-6 rounded-[6px] px-1.5 text-[13px] leading-[14px] tracking-[0.01em] [--control-padding-inline:0.375rem]",
|
|
4
|
+
sm: "h-7 rounded-[6px] px-2 text-sm leading-5 tracking-[0.01em] [--control-padding-inline:0.5rem]",
|
|
5
|
+
md: "h-8 rounded-[8px] px-[11px] text-sm leading-[15px] tracking-[0.01em] [--control-padding-inline:11px]",
|
|
6
|
+
lg: "h-9 rounded-[8px] px-[13px] text-base leading-[15px] [--control-padding-inline:13px]",
|
|
7
|
+
xl: "h-12 rounded-[10px] px-[18px] text-base leading-5 [--control-padding-inline:18px]"
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as controlSizeClassNames };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from '../components/avatar';
|
|
2
|
+
export * from '../components/badge';
|
|
3
|
+
export * from '../components/button';
|
|
4
|
+
export * from '../components/card';
|
|
5
|
+
export * from '../components/dropdown-menu';
|
|
6
|
+
export * from '../components/field';
|
|
7
|
+
export * from '../components/input';
|
|
8
|
+
export * from '../components/input-group';
|
|
9
|
+
export * from '../components/identity-mark';
|
|
10
|
+
export * from '../components/navigation-action';
|
|
11
|
+
export * from '../components/progress';
|
|
12
|
+
export * from '../components/select';
|
|
13
|
+
export * from '../components/sidebar';
|
|
14
|
+
export * from '../components/table';
|
|
15
|
+
export * from '../components/textarea';
|
|
16
|
+
export * from '../components/tooltip';
|
|
17
|
+
export { cn } from '../lib/utils';
|
|
18
|
+
//# sourceMappingURL=recipe-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipe-runtime.d.ts","sourceRoot":"","sources":["../../src/recipes/recipe-runtime.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AACpC,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almanaclabs/blaze-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared React components and design tokens for Blaze",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
],
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
+
"recipes",
|
|
11
12
|
"src/styles",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Recipes
|
|
2
|
+
|
|
3
|
+
Recipes are executable reference implementations for designers and AI agents. They
|
|
4
|
+
ship with the npm package and show how Blaze UI primitives combine into recognizable
|
|
5
|
+
product surfaces without becoming part of the package's runtime API.
|
|
6
|
+
|
|
7
|
+
Use a recipe when the page shares its anatomy and interaction model. Copy or adapt
|
|
8
|
+
the composition into the application, replace fictional data, and connect links and
|
|
9
|
+
actions through the application's framework conventions.
|
|
10
|
+
|
|
11
|
+
Installed recipes are available under
|
|
12
|
+
`node_modules/@almanaclabs/blaze-ui/recipes`. Start with `manifest.json` when
|
|
13
|
+
discovering recipes programmatically.
|
|
14
|
+
|
|
15
|
+
Preserve the recipe's semantic landmarks, responsive behavior, Blaze UI primitives,
|
|
16
|
+
and semantic tokens. Do not copy fixture data or introduce application routing,
|
|
17
|
+
authorization, serializers, or business rules into this directory.
|
|
18
|
+
|
|
19
|
+
## Available recipes
|
|
20
|
+
|
|
21
|
+
- [Workspace shell](workspace-shell/README.md): collapsible navigation, workspace
|
|
22
|
+
identity, page header, sub-navigation, account access, and a page-content slot.
|
|
23
|
+
- [Guided workflow](guided-workflow/README.md): ordered step progress, contextual
|
|
24
|
+
work-item navigation, a focused canvas, and persistent workflow actions.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Guided workflow recipe
|
|
2
|
+
|
|
3
|
+
Use this recipe for multi-step creation and review experiences where people move
|
|
4
|
+
between related work items, refine domain-specific content in a focused canvas,
|
|
5
|
+
and advance through a gated sequence.
|
|
6
|
+
|
|
7
|
+
## Preserve
|
|
8
|
+
|
|
9
|
+
- A compact, ordered progress landmark with the active step marked by
|
|
10
|
+
`aria-current="step"`.
|
|
11
|
+
- One controlled contextual rail for switching among sibling work items.
|
|
12
|
+
- A single scrollable canvas for the selected item's domain content.
|
|
13
|
+
- Back, secondary, status, and primary actions in a persistent workflow footer.
|
|
14
|
+
- Responsive behavior that moves the rail above the canvas on smaller screens.
|
|
15
|
+
- Semantic Blaze UI colors, spacing, focus treatment, and public components.
|
|
16
|
+
- Divider-separated `GuidedWorkflowSection` blocks for task questions that share
|
|
17
|
+
one continuous canvas instead of behaving like standalone cards.
|
|
18
|
+
|
|
19
|
+
## Adapt
|
|
20
|
+
|
|
21
|
+
- Replace the fictional Storybook examples with serialized application data.
|
|
22
|
+
- Keep step state, selected-item state, validation, and permissions in the app.
|
|
23
|
+
- Compose forms, review tables, previews, and approval documents inside the
|
|
24
|
+
`children` canvas slot with public Blaze UI primitives.
|
|
25
|
+
- Use `variant="compact"` when the workflow sits inside an established product
|
|
26
|
+
shell and needs the Blaze 3 title, compact stepper, canonical 340px rail, and
|
|
27
|
+
full-width white task canvas.
|
|
28
|
+
- In `WorkspaceShellRecipe mode="focused"`, compose
|
|
29
|
+
`GuidedWorkflowPageHeader` into `pageHeader`, `GuidedWorkflowBody` into
|
|
30
|
+
`children`, and `GuidedWorkflowFooter` into `footer`. The workspace shell owns
|
|
31
|
+
the product identity row and its top-right exit action.
|
|
32
|
+
- Pass only semantic or application-owned classes through `markerClassName`;
|
|
33
|
+
the section component owns marker geometry but not product category colors.
|
|
34
|
+
- Connect callbacks to the application's routing and persistence conventions.
|
|
35
|
+
- Use item statuses only for concise orientation; put actionable validation in
|
|
36
|
+
the canvas beside the field or object that needs attention.
|
|
37
|
+
|
|
38
|
+
## Avoid
|
|
39
|
+
|
|
40
|
+
- Importing this recipe as a runtime package component.
|
|
41
|
+
- Moving forecasting, validation, approval policy, routing, or data fetching into
|
|
42
|
+
the copied recipe.
|
|
43
|
+
- Turning channel-specific objects or third-party previews into recipe props.
|
|
44
|
+
- Adding a second page shell around the recipe. Use the exported workflow regions
|
|
45
|
+
inside the application's existing authenticated shell; reserve the all-in-one
|
|
46
|
+
`GuidedWorkflowRecipe` for standalone examples and shell-less contexts.
|
|
47
|
+
- Hiding required work behind color alone or allowing a disabled primary action
|
|
48
|
+
without nearby explanatory status text.
|
|
49
|
+
|
|
50
|
+
Start from `guided-workflow.tsx`. Review `Recipes/Guided workflow` in Storybook for
|
|
51
|
+
campaign-planning, workspace-onboarding, mobile, and dark-mode examples.
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
IconAlertCircle,
|
|
4
|
+
IconCheck,
|
|
5
|
+
IconChevronRight,
|
|
6
|
+
} from '@tabler/icons-react'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Badge,
|
|
10
|
+
Button,
|
|
11
|
+
NavigationAction,
|
|
12
|
+
Progress,
|
|
13
|
+
cn,
|
|
14
|
+
} from '@almanaclabs/blaze-ui'
|
|
15
|
+
|
|
16
|
+
export type GuidedWorkflowStep = {
|
|
17
|
+
key: string
|
|
18
|
+
label: string
|
|
19
|
+
description?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type GuidedWorkflowItemStatus = 'complete' | 'attention' | 'pending'
|
|
23
|
+
|
|
24
|
+
export type GuidedWorkflowItem = {
|
|
25
|
+
key: string
|
|
26
|
+
label: string
|
|
27
|
+
description?: string
|
|
28
|
+
status?: GuidedWorkflowItemStatus
|
|
29
|
+
statusLabel?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type GuidedWorkflowVariant = 'default' | 'compact'
|
|
33
|
+
|
|
34
|
+
export type GuidedWorkflowRecipeProps = {
|
|
35
|
+
title: string
|
|
36
|
+
steps: readonly GuidedWorkflowStep[]
|
|
37
|
+
activeStepKey: string
|
|
38
|
+
railLabel: string
|
|
39
|
+
items: readonly GuidedWorkflowItem[]
|
|
40
|
+
activeItemKey: string
|
|
41
|
+
onItemSelect: (key: string) => void
|
|
42
|
+
children: ReactNode
|
|
43
|
+
backLabel?: string
|
|
44
|
+
continueLabel?: string
|
|
45
|
+
continueDisabled?: boolean
|
|
46
|
+
footerStatus?: ReactNode
|
|
47
|
+
secondaryActions?: ReactNode
|
|
48
|
+
onBack?: () => void
|
|
49
|
+
onContinue: () => void
|
|
50
|
+
variant?: GuidedWorkflowVariant
|
|
51
|
+
className?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type GuidedWorkflowPageHeaderProps = {
|
|
55
|
+
title: string
|
|
56
|
+
steps: readonly GuidedWorkflowStep[]
|
|
57
|
+
activeStepKey: string
|
|
58
|
+
variant?: GuidedWorkflowVariant
|
|
59
|
+
className?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type GuidedWorkflowBodyProps = {
|
|
63
|
+
railLabel: string
|
|
64
|
+
items: readonly GuidedWorkflowItem[]
|
|
65
|
+
activeItemKey: string
|
|
66
|
+
onItemSelect: (key: string) => void
|
|
67
|
+
children: ReactNode
|
|
68
|
+
contentLabel?: string
|
|
69
|
+
variant?: GuidedWorkflowVariant
|
|
70
|
+
className?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type GuidedWorkflowFooterProps = {
|
|
74
|
+
backLabel?: string
|
|
75
|
+
continueLabel?: string
|
|
76
|
+
continueDisabled?: boolean
|
|
77
|
+
footerStatus?: ReactNode
|
|
78
|
+
secondaryActions?: ReactNode
|
|
79
|
+
onBack?: () => void
|
|
80
|
+
onContinue: () => void
|
|
81
|
+
variant?: GuidedWorkflowVariant
|
|
82
|
+
className?: string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type GuidedWorkflowSectionProps = {
|
|
86
|
+
title: ReactNode
|
|
87
|
+
icon?: ReactNode
|
|
88
|
+
description?: ReactNode
|
|
89
|
+
action?: ReactNode
|
|
90
|
+
children: ReactNode
|
|
91
|
+
className?: string
|
|
92
|
+
markerClassName?: string
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const statusConfiguration = {
|
|
96
|
+
attention: {
|
|
97
|
+
fallbackLabel: 'Needs attention',
|
|
98
|
+
icon: IconAlertCircle,
|
|
99
|
+
variant: 'destructive',
|
|
100
|
+
},
|
|
101
|
+
complete: {
|
|
102
|
+
fallbackLabel: 'Complete',
|
|
103
|
+
icon: IconCheck,
|
|
104
|
+
variant: 'success',
|
|
105
|
+
},
|
|
106
|
+
pending: {
|
|
107
|
+
fallbackLabel: 'Pending',
|
|
108
|
+
icon: null,
|
|
109
|
+
variant: 'secondary',
|
|
110
|
+
},
|
|
111
|
+
} as const
|
|
112
|
+
|
|
113
|
+
function WorkflowItemStatus({ item }: { item: GuidedWorkflowItem }) {
|
|
114
|
+
if (!item.status) return null
|
|
115
|
+
|
|
116
|
+
const configuration = statusConfiguration[item.status]
|
|
117
|
+
const Icon = configuration.icon
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<Badge variant={configuration.variant}>
|
|
121
|
+
{Icon ? <Icon data-icon="inline-start" aria-hidden="true" /> : null}
|
|
122
|
+
{item.statusLabel ?? configuration.fallbackLabel}
|
|
123
|
+
</Badge>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function GuidedWorkflowSections({
|
|
128
|
+
className,
|
|
129
|
+
...props
|
|
130
|
+
}: React.ComponentProps<'div'>) {
|
|
131
|
+
return <div className={cn('grid gap-8', className)} {...props} />
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function GuidedWorkflowSection({
|
|
135
|
+
title,
|
|
136
|
+
icon,
|
|
137
|
+
description,
|
|
138
|
+
action,
|
|
139
|
+
children,
|
|
140
|
+
className,
|
|
141
|
+
markerClassName,
|
|
142
|
+
}: GuidedWorkflowSectionProps) {
|
|
143
|
+
return (
|
|
144
|
+
<section
|
|
145
|
+
className={cn(
|
|
146
|
+
'grid gap-5 border-b border-border pb-8 last:border-b-0 last:pb-0',
|
|
147
|
+
className,
|
|
148
|
+
)}
|
|
149
|
+
>
|
|
150
|
+
<header className="flex min-w-0 items-start justify-between gap-4">
|
|
151
|
+
<div className="flex min-w-0 items-center gap-2.5">
|
|
152
|
+
{icon ? (
|
|
153
|
+
<span
|
|
154
|
+
className={cn(
|
|
155
|
+
'grid size-8 shrink-0 place-items-center rounded-full text-primary-foreground [--icon-stroke-width:var(--icon-stroke-16)] [&>svg:not([class*=size-])]:size-4',
|
|
156
|
+
markerClassName,
|
|
157
|
+
)}
|
|
158
|
+
aria-hidden="true"
|
|
159
|
+
>
|
|
160
|
+
{icon}
|
|
161
|
+
</span>
|
|
162
|
+
) : null}
|
|
163
|
+
<div className="min-w-0">
|
|
164
|
+
<h2 className="type-section-title font-normal text-foreground">
|
|
165
|
+
{title}
|
|
166
|
+
</h2>
|
|
167
|
+
{description ? (
|
|
168
|
+
<p className="type-body mt-1 text-muted-foreground">
|
|
169
|
+
{description}
|
|
170
|
+
</p>
|
|
171
|
+
) : null}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
{action}
|
|
175
|
+
</header>
|
|
176
|
+
<div className="min-w-0">{children}</div>
|
|
177
|
+
</section>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getActiveStepIndex(
|
|
182
|
+
steps: readonly GuidedWorkflowStep[],
|
|
183
|
+
activeStepKey: string,
|
|
184
|
+
) {
|
|
185
|
+
return Math.max(
|
|
186
|
+
0,
|
|
187
|
+
steps.findIndex((step) => step.key === activeStepKey),
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function GuidedWorkflowPageHeader({
|
|
192
|
+
title,
|
|
193
|
+
steps,
|
|
194
|
+
activeStepKey,
|
|
195
|
+
variant = 'default',
|
|
196
|
+
className,
|
|
197
|
+
}: GuidedWorkflowPageHeaderProps) {
|
|
198
|
+
const activeStepIndex = getActiveStepIndex(steps, activeStepKey)
|
|
199
|
+
const activeStep = steps[activeStepIndex]
|
|
200
|
+
const progressValue =
|
|
201
|
+
steps.length === 0 ? 0 : ((activeStepIndex + 1) / steps.length) * 100
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<header
|
|
205
|
+
className={cn(
|
|
206
|
+
'border-b bg-background px-4 md:px-6',
|
|
207
|
+
variant === 'compact' ? 'flex min-h-[76px] items-center py-3' : 'py-4',
|
|
208
|
+
className,
|
|
209
|
+
)}
|
|
210
|
+
>
|
|
211
|
+
<div className="flex w-full flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
|
|
212
|
+
<div className="min-w-0">
|
|
213
|
+
{variant === 'default' ? (
|
|
214
|
+
<p className="type-meta text-muted-foreground uppercase">
|
|
215
|
+
Step {steps.length === 0 ? 0 : activeStepIndex + 1} of{' '}
|
|
216
|
+
{steps.length}
|
|
217
|
+
</p>
|
|
218
|
+
) : null}
|
|
219
|
+
<h1
|
|
220
|
+
className={cn(
|
|
221
|
+
'type-page-title truncate font-normal',
|
|
222
|
+
variant === 'default' && 'mt-1',
|
|
223
|
+
)}
|
|
224
|
+
>
|
|
225
|
+
{title}
|
|
226
|
+
</h1>
|
|
227
|
+
{variant === 'default' && activeStep?.description ? (
|
|
228
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
229
|
+
{activeStep.description}
|
|
230
|
+
</p>
|
|
231
|
+
) : null}
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<nav aria-label="Workflow progress" className="min-w-0 xl:w-3/5">
|
|
235
|
+
<ol className="type-meta flex min-w-max items-center gap-2 overflow-x-auto pb-1 sm:min-w-0 sm:justify-end">
|
|
236
|
+
{steps.map((step, index) => {
|
|
237
|
+
const complete = index < activeStepIndex
|
|
238
|
+
const active = index === activeStepIndex
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<li
|
|
242
|
+
key={step.key}
|
|
243
|
+
aria-current={active ? 'step' : undefined}
|
|
244
|
+
className={cn(
|
|
245
|
+
'flex items-center gap-2 text-muted-foreground',
|
|
246
|
+
active && 'font-medium text-foreground',
|
|
247
|
+
)}
|
|
248
|
+
>
|
|
249
|
+
<span
|
|
250
|
+
className={cn(
|
|
251
|
+
'grid shrink-0 place-items-center border bg-muted',
|
|
252
|
+
variant === 'compact'
|
|
253
|
+
? 'size-4 rounded-[4px] text-[0.625rem]'
|
|
254
|
+
: 'size-5 rounded-md text-[0.65rem]',
|
|
255
|
+
active && 'border-foreground/40 bg-background',
|
|
256
|
+
complete &&
|
|
257
|
+
variant === 'default' &&
|
|
258
|
+
'border-primary bg-primary text-primary-foreground',
|
|
259
|
+
)}
|
|
260
|
+
aria-hidden="true"
|
|
261
|
+
>
|
|
262
|
+
{complete && variant === 'default' ? (
|
|
263
|
+
<IconCheck className="size-3" />
|
|
264
|
+
) : (
|
|
265
|
+
index + 1
|
|
266
|
+
)}
|
|
267
|
+
</span>
|
|
268
|
+
<span>{step.label}</span>
|
|
269
|
+
{index < steps.length - 1 ? (
|
|
270
|
+
<IconChevronRight
|
|
271
|
+
className="size-3.5 text-border"
|
|
272
|
+
aria-hidden="true"
|
|
273
|
+
/>
|
|
274
|
+
) : null}
|
|
275
|
+
</li>
|
|
276
|
+
)
|
|
277
|
+
})}
|
|
278
|
+
</ol>
|
|
279
|
+
{variant === 'default' ? (
|
|
280
|
+
<Progress
|
|
281
|
+
value={progressValue}
|
|
282
|
+
aria-label={`${title} progress`}
|
|
283
|
+
className="mt-2 gap-0"
|
|
284
|
+
/>
|
|
285
|
+
) : null}
|
|
286
|
+
</nav>
|
|
287
|
+
</div>
|
|
288
|
+
</header>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function GuidedWorkflowBody({
|
|
293
|
+
railLabel,
|
|
294
|
+
items,
|
|
295
|
+
activeItemKey,
|
|
296
|
+
onItemSelect,
|
|
297
|
+
children,
|
|
298
|
+
contentLabel,
|
|
299
|
+
variant = 'default',
|
|
300
|
+
className,
|
|
301
|
+
}: GuidedWorkflowBodyProps) {
|
|
302
|
+
return (
|
|
303
|
+
<div
|
|
304
|
+
className={cn(
|
|
305
|
+
'grid min-h-0 grid-rows-[auto_minmax(0,1fr)]',
|
|
306
|
+
variant === 'compact'
|
|
307
|
+
? 'md:grid-cols-[21.25rem_minmax(0,1fr)] md:grid-rows-1'
|
|
308
|
+
: 'lg:grid-cols-[20rem_minmax(0,1fr)] lg:grid-rows-1',
|
|
309
|
+
className,
|
|
310
|
+
)}
|
|
311
|
+
>
|
|
312
|
+
<aside
|
|
313
|
+
className={cn(
|
|
314
|
+
'min-w-0 border-b',
|
|
315
|
+
variant === 'compact'
|
|
316
|
+
? 'bg-background md:border-r md:border-b-0'
|
|
317
|
+
: 'bg-surface-subtle lg:border-r lg:border-b-0',
|
|
318
|
+
)}
|
|
319
|
+
>
|
|
320
|
+
<nav
|
|
321
|
+
aria-label={railLabel}
|
|
322
|
+
className={cn(
|
|
323
|
+
'flex gap-2 overflow-x-auto p-3',
|
|
324
|
+
variant === 'compact'
|
|
325
|
+
? 'md:grid md:max-h-full md:overflow-y-auto md:p-6'
|
|
326
|
+
: 'lg:grid lg:max-h-full lg:overflow-y-auto lg:p-5',
|
|
327
|
+
)}
|
|
328
|
+
>
|
|
329
|
+
<p
|
|
330
|
+
className={cn(
|
|
331
|
+
'type-label hidden px-2 font-medium text-foreground',
|
|
332
|
+
variant === 'compact' ? 'md:block' : 'lg:block',
|
|
333
|
+
)}
|
|
334
|
+
>
|
|
335
|
+
{railLabel}
|
|
336
|
+
</p>
|
|
337
|
+
{items.map((item) => {
|
|
338
|
+
const selected = item.key === activeItemKey
|
|
339
|
+
|
|
340
|
+
return (
|
|
341
|
+
<NavigationAction
|
|
342
|
+
key={item.key}
|
|
343
|
+
type="button"
|
|
344
|
+
aria-current={selected ? 'page' : undefined}
|
|
345
|
+
onClick={() => onItemSelect(item.key)}
|
|
346
|
+
className={cn(
|
|
347
|
+
'h-auto min-w-64 items-start justify-start whitespace-normal border border-transparent px-3 py-3 text-left',
|
|
348
|
+
variant === 'compact'
|
|
349
|
+
? 'md:min-w-0 md:w-full'
|
|
350
|
+
: 'lg:min-w-0 lg:w-full',
|
|
351
|
+
variant === 'compact' && 'rounded-sm md:min-h-19 md:px-5',
|
|
352
|
+
)}
|
|
353
|
+
>
|
|
354
|
+
<span className="grid min-w-0 flex-1 gap-1">
|
|
355
|
+
<span className="type-content truncate font-normal">
|
|
356
|
+
{item.label}
|
|
357
|
+
</span>
|
|
358
|
+
{item.description ? (
|
|
359
|
+
<span className="type-body truncate font-normal text-muted-foreground">
|
|
360
|
+
{item.description}
|
|
361
|
+
</span>
|
|
362
|
+
) : null}
|
|
363
|
+
<WorkflowItemStatus item={item} />
|
|
364
|
+
</span>
|
|
365
|
+
</NavigationAction>
|
|
366
|
+
)
|
|
367
|
+
})}
|
|
368
|
+
</nav>
|
|
369
|
+
</aside>
|
|
370
|
+
|
|
371
|
+
<section
|
|
372
|
+
aria-label={contentLabel ?? railLabel}
|
|
373
|
+
className={cn(
|
|
374
|
+
'min-w-0 overflow-y-auto',
|
|
375
|
+
variant === 'compact' ? 'bg-background' : 'bg-surface-subtle',
|
|
376
|
+
)}
|
|
377
|
+
>
|
|
378
|
+
<div
|
|
379
|
+
className={cn(
|
|
380
|
+
'mx-auto w-full p-4 md:p-8',
|
|
381
|
+
variant === 'compact' ? 'max-w-none md:p-6' : 'max-w-5xl lg:p-10',
|
|
382
|
+
)}
|
|
383
|
+
>
|
|
384
|
+
{children}
|
|
385
|
+
</div>
|
|
386
|
+
</section>
|
|
387
|
+
</div>
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function GuidedWorkflowFooter({
|
|
392
|
+
backLabel = 'Back',
|
|
393
|
+
continueLabel = 'Continue',
|
|
394
|
+
continueDisabled = false,
|
|
395
|
+
footerStatus,
|
|
396
|
+
secondaryActions,
|
|
397
|
+
onBack,
|
|
398
|
+
onContinue,
|
|
399
|
+
variant = 'default',
|
|
400
|
+
className,
|
|
401
|
+
}: GuidedWorkflowFooterProps) {
|
|
402
|
+
return (
|
|
403
|
+
<footer
|
|
404
|
+
className={cn(
|
|
405
|
+
'flex flex-wrap items-center justify-between gap-3 border-t bg-background px-4 py-3 md:px-6',
|
|
406
|
+
variant === 'compact' ? 'min-h-[88px]' : 'min-h-16',
|
|
407
|
+
className,
|
|
408
|
+
)}
|
|
409
|
+
>
|
|
410
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
411
|
+
{onBack ? (
|
|
412
|
+
<Button type="button" variant="secondary" size="md" onClick={onBack}>
|
|
413
|
+
{backLabel}
|
|
414
|
+
</Button>
|
|
415
|
+
) : null}
|
|
416
|
+
{footerStatus ? (
|
|
417
|
+
<div className="truncate text-sm text-muted-foreground">
|
|
418
|
+
{footerStatus}
|
|
419
|
+
</div>
|
|
420
|
+
) : null}
|
|
421
|
+
</div>
|
|
422
|
+
<div className="flex items-center gap-2">
|
|
423
|
+
{secondaryActions}
|
|
424
|
+
<Button
|
|
425
|
+
type="button"
|
|
426
|
+
size="md"
|
|
427
|
+
disabled={continueDisabled}
|
|
428
|
+
onClick={onContinue}
|
|
429
|
+
>
|
|
430
|
+
{continueLabel}
|
|
431
|
+
<IconChevronRight data-icon="inline-end" aria-hidden="true" />
|
|
432
|
+
</Button>
|
|
433
|
+
</div>
|
|
434
|
+
</footer>
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function GuidedWorkflowRecipe({
|
|
439
|
+
title,
|
|
440
|
+
steps,
|
|
441
|
+
activeStepKey,
|
|
442
|
+
railLabel,
|
|
443
|
+
items,
|
|
444
|
+
activeItemKey,
|
|
445
|
+
onItemSelect,
|
|
446
|
+
children,
|
|
447
|
+
backLabel = 'Back',
|
|
448
|
+
continueLabel = 'Continue',
|
|
449
|
+
continueDisabled = false,
|
|
450
|
+
footerStatus,
|
|
451
|
+
secondaryActions,
|
|
452
|
+
onBack,
|
|
453
|
+
onContinue,
|
|
454
|
+
variant = 'default',
|
|
455
|
+
className,
|
|
456
|
+
}: GuidedWorkflowRecipeProps) {
|
|
457
|
+
const activeStepIndex = getActiveStepIndex(steps, activeStepKey)
|
|
458
|
+
const activeStep = steps[activeStepIndex]
|
|
459
|
+
|
|
460
|
+
return (
|
|
461
|
+
<section
|
|
462
|
+
className={cn(
|
|
463
|
+
'grid h-dvh min-h-[720px] grid-rows-[auto_minmax(0,1fr)_auto] overflow-hidden bg-background text-foreground',
|
|
464
|
+
className,
|
|
465
|
+
)}
|
|
466
|
+
>
|
|
467
|
+
<GuidedWorkflowPageHeader
|
|
468
|
+
title={title}
|
|
469
|
+
steps={steps}
|
|
470
|
+
activeStepKey={activeStepKey}
|
|
471
|
+
variant={variant}
|
|
472
|
+
/>
|
|
473
|
+
<GuidedWorkflowBody
|
|
474
|
+
railLabel={railLabel}
|
|
475
|
+
items={items}
|
|
476
|
+
activeItemKey={activeItemKey}
|
|
477
|
+
onItemSelect={onItemSelect}
|
|
478
|
+
contentLabel={activeStep?.label ?? title}
|
|
479
|
+
variant={variant}
|
|
480
|
+
>
|
|
481
|
+
{children}
|
|
482
|
+
</GuidedWorkflowBody>
|
|
483
|
+
<GuidedWorkflowFooter
|
|
484
|
+
backLabel={backLabel}
|
|
485
|
+
continueLabel={continueLabel}
|
|
486
|
+
continueDisabled={continueDisabled}
|
|
487
|
+
footerStatus={footerStatus}
|
|
488
|
+
secondaryActions={secondaryActions}
|
|
489
|
+
onBack={onBack}
|
|
490
|
+
onContinue={onContinue}
|
|
491
|
+
variant={variant}
|
|
492
|
+
/>
|
|
493
|
+
</section>
|
|
494
|
+
)
|
|
495
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"recipes": [
|
|
4
|
+
{
|
|
5
|
+
"name": "workspace-shell",
|
|
6
|
+
"description": "Authenticated Blaze workspace chrome with shared standard and focused modes, replaceable identity, page chrome, and pinned actions.",
|
|
7
|
+
"dependencies": ["@almanaclabs/blaze-ui", "@tabler/icons-react"],
|
|
8
|
+
"entry": "workspace-shell/workspace-shell.tsx",
|
|
9
|
+
"instructions": "workspace-shell/README.md"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "guided-workflow",
|
|
13
|
+
"description": "Multi-step creation and review flow with ordered progress, contextual work-item navigation, a focused canvas, and persistent actions.",
|
|
14
|
+
"dependencies": ["@almanaclabs/blaze-ui", "@tabler/icons-react"],
|
|
15
|
+
"entry": "guided-workflow/guided-workflow.tsx",
|
|
16
|
+
"instructions": "guided-workflow/README.md"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|