@girardmedia/bootspring 1.2.0 → 2.0.3
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 +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
# Input Component Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for form input components.
|
|
4
|
-
|
|
5
|
-
## Text Input
|
|
6
|
-
|
|
7
|
-
```tsx
|
|
8
|
-
// components/ui/Input.tsx
|
|
9
|
-
import { forwardRef } from 'react'
|
|
10
|
-
|
|
11
|
-
interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
12
|
-
label?: string
|
|
13
|
-
error?: string
|
|
14
|
-
hint?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const Input = forwardRef<HTMLInputElement, Props>(
|
|
18
|
-
({ label, error, hint, className = '', ...props }, ref) => {
|
|
19
|
-
return (
|
|
20
|
-
<div className="space-y-1">
|
|
21
|
-
{label && (
|
|
22
|
-
<label className="block text-sm font-medium text-gray-700">
|
|
23
|
-
{label}
|
|
24
|
-
{props.required && <span className="text-red-500">*</span>}
|
|
25
|
-
</label>
|
|
26
|
-
)}
|
|
27
|
-
|
|
28
|
-
<input
|
|
29
|
-
ref={ref}
|
|
30
|
-
className={`w-full rounded-lg border px-3 py-2 outline-none transition-colors
|
|
31
|
-
${error
|
|
32
|
-
? 'border-red-500 focus:border-red-500 focus:ring-1 focus:ring-red-500'
|
|
33
|
-
: 'border-gray-300 focus:border-blue-500 focus:ring-1 focus:ring-blue-500'
|
|
34
|
-
}
|
|
35
|
-
disabled:cursor-not-allowed disabled:bg-gray-100
|
|
36
|
-
${className}`}
|
|
37
|
-
{...props}
|
|
38
|
-
/>
|
|
39
|
-
|
|
40
|
-
{hint && !error && (
|
|
41
|
-
<p className="text-sm text-gray-500">{hint}</p>
|
|
42
|
-
)}
|
|
43
|
-
|
|
44
|
-
{error && (
|
|
45
|
-
<p className="text-sm text-red-500">{error}</p>
|
|
46
|
-
)}
|
|
47
|
-
</div>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
Input.displayName = 'Input'
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Select
|
|
56
|
-
|
|
57
|
-
```tsx
|
|
58
|
-
// components/ui/Select.tsx
|
|
59
|
-
import { forwardRef } from 'react'
|
|
60
|
-
import { ChevronDown } from 'lucide-react'
|
|
61
|
-
|
|
62
|
-
interface Option {
|
|
63
|
-
value: string
|
|
64
|
-
label: string
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
interface Props extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
|
68
|
-
label?: string
|
|
69
|
-
error?: string
|
|
70
|
-
options: Option[]
|
|
71
|
-
placeholder?: string
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const Select = forwardRef<HTMLSelectElement, Props>(
|
|
75
|
-
({ label, error, options, placeholder, className = '', ...props }, ref) => {
|
|
76
|
-
return (
|
|
77
|
-
<div className="space-y-1">
|
|
78
|
-
{label && (
|
|
79
|
-
<label className="block text-sm font-medium text-gray-700">
|
|
80
|
-
{label}
|
|
81
|
-
</label>
|
|
82
|
-
)}
|
|
83
|
-
|
|
84
|
-
<div className="relative">
|
|
85
|
-
<select
|
|
86
|
-
ref={ref}
|
|
87
|
-
className={`w-full appearance-none rounded-lg border bg-white px-3 py-2 pr-10 outline-none
|
|
88
|
-
${error
|
|
89
|
-
? 'border-red-500 focus:border-red-500'
|
|
90
|
-
: 'border-gray-300 focus:border-blue-500'
|
|
91
|
-
}
|
|
92
|
-
disabled:cursor-not-allowed disabled:bg-gray-100
|
|
93
|
-
${className}`}
|
|
94
|
-
{...props}
|
|
95
|
-
>
|
|
96
|
-
{placeholder && (
|
|
97
|
-
<option value="" disabled>
|
|
98
|
-
{placeholder}
|
|
99
|
-
</option>
|
|
100
|
-
)}
|
|
101
|
-
{options.map(option => (
|
|
102
|
-
<option key={option.value} value={option.value}>
|
|
103
|
-
{option.label}
|
|
104
|
-
</option>
|
|
105
|
-
))}
|
|
106
|
-
</select>
|
|
107
|
-
|
|
108
|
-
<ChevronDown className="pointer-events-none absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
{error && <p className="text-sm text-red-500">{error}</p>}
|
|
112
|
-
</div>
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
Select.displayName = 'Select'
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Checkbox
|
|
121
|
-
|
|
122
|
-
```tsx
|
|
123
|
-
// components/ui/Checkbox.tsx
|
|
124
|
-
import { forwardRef } from 'react'
|
|
125
|
-
import { Check } from 'lucide-react'
|
|
126
|
-
|
|
127
|
-
interface Props extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
128
|
-
label: string
|
|
129
|
-
description?: string
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export const Checkbox = forwardRef<HTMLInputElement, Props>(
|
|
133
|
-
({ label, description, className = '', ...props }, ref) => {
|
|
134
|
-
return (
|
|
135
|
-
<label className="flex cursor-pointer items-start gap-3">
|
|
136
|
-
<div className="relative mt-0.5">
|
|
137
|
-
<input
|
|
138
|
-
ref={ref}
|
|
139
|
-
type="checkbox"
|
|
140
|
-
className="peer sr-only"
|
|
141
|
-
{...props}
|
|
142
|
-
/>
|
|
143
|
-
<div className="h-5 w-5 rounded border border-gray-300 bg-white transition-colors peer-checked:border-blue-600 peer-checked:bg-blue-600 peer-focus:ring-2 peer-focus:ring-blue-500 peer-focus:ring-offset-2 peer-disabled:cursor-not-allowed peer-disabled:opacity-50">
|
|
144
|
-
<Check className="h-full w-full p-0.5 text-white opacity-0 peer-checked:opacity-100" />
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
<div>
|
|
149
|
-
<span className="text-sm font-medium text-gray-900">{label}</span>
|
|
150
|
-
{description && (
|
|
151
|
-
<p className="text-sm text-gray-500">{description}</p>
|
|
152
|
-
)}
|
|
153
|
-
</div>
|
|
154
|
-
</label>
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
Checkbox.displayName = 'Checkbox'
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Radio Group
|
|
163
|
-
|
|
164
|
-
```tsx
|
|
165
|
-
// components/ui/RadioGroup.tsx
|
|
166
|
-
interface Option {
|
|
167
|
-
value: string
|
|
168
|
-
label: string
|
|
169
|
-
description?: string
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
interface Props {
|
|
173
|
-
name: string
|
|
174
|
-
options: Option[]
|
|
175
|
-
value?: string
|
|
176
|
-
onChange?: (value: string) => void
|
|
177
|
-
label?: string
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export function RadioGroup({ name, options, value, onChange, label }: Props) {
|
|
181
|
-
return (
|
|
182
|
-
<fieldset className="space-y-3">
|
|
183
|
-
{label && (
|
|
184
|
-
<legend className="text-sm font-medium text-gray-700">{label}</legend>
|
|
185
|
-
)}
|
|
186
|
-
|
|
187
|
-
{options.map(option => (
|
|
188
|
-
<label key={option.value} className="flex cursor-pointer items-start gap-3">
|
|
189
|
-
<div className="relative mt-0.5">
|
|
190
|
-
<input
|
|
191
|
-
type="radio"
|
|
192
|
-
name={name}
|
|
193
|
-
value={option.value}
|
|
194
|
-
checked={value === option.value}
|
|
195
|
-
onChange={() => onChange?.(option.value)}
|
|
196
|
-
className="peer sr-only"
|
|
197
|
-
/>
|
|
198
|
-
<div className="h-5 w-5 rounded-full border-2 border-gray-300 bg-white transition-colors peer-checked:border-blue-600 peer-focus:ring-2 peer-focus:ring-blue-500 peer-focus:ring-offset-2">
|
|
199
|
-
<div className="absolute left-1/2 top-1/2 h-2.5 w-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-blue-600 opacity-0 peer-checked:opacity-100" />
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
|
|
203
|
-
<div>
|
|
204
|
-
<span className="text-sm font-medium text-gray-900">{option.label}</span>
|
|
205
|
-
{option.description && (
|
|
206
|
-
<p className="text-sm text-gray-500">{option.description}</p>
|
|
207
|
-
)}
|
|
208
|
-
</div>
|
|
209
|
-
</label>
|
|
210
|
-
))}
|
|
211
|
-
</fieldset>
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
## Switch / Toggle
|
|
217
|
-
|
|
218
|
-
```tsx
|
|
219
|
-
// components/ui/Switch.tsx
|
|
220
|
-
import * as SwitchPrimitive from '@radix-ui/react-switch'
|
|
221
|
-
|
|
222
|
-
interface Props {
|
|
223
|
-
checked?: boolean
|
|
224
|
-
onCheckedChange?: (checked: boolean) => void
|
|
225
|
-
label?: string
|
|
226
|
-
description?: string
|
|
227
|
-
disabled?: boolean
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export function Switch({ checked, onCheckedChange, label, description, disabled }: Props) {
|
|
231
|
-
return (
|
|
232
|
-
<div className="flex items-center justify-between">
|
|
233
|
-
{(label || description) && (
|
|
234
|
-
<div>
|
|
235
|
-
{label && <p className="text-sm font-medium">{label}</p>}
|
|
236
|
-
{description && <p className="text-sm text-gray-500">{description}</p>}
|
|
237
|
-
</div>
|
|
238
|
-
)}
|
|
239
|
-
|
|
240
|
-
<SwitchPrimitive.Root
|
|
241
|
-
checked={checked}
|
|
242
|
-
onCheckedChange={onCheckedChange}
|
|
243
|
-
disabled={disabled}
|
|
244
|
-
className="relative h-6 w-11 cursor-pointer rounded-full bg-gray-200 transition-colors data-[state=checked]:bg-blue-600 disabled:cursor-not-allowed disabled:opacity-50"
|
|
245
|
-
>
|
|
246
|
-
<SwitchPrimitive.Thumb className="block h-5 w-5 translate-x-0.5 rounded-full bg-white shadow-sm transition-transform data-[state=checked]:translate-x-[22px]" />
|
|
247
|
-
</SwitchPrimitive.Root>
|
|
248
|
-
</div>
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
## Search Input
|
|
254
|
-
|
|
255
|
-
```tsx
|
|
256
|
-
// components/ui/SearchInput.tsx
|
|
257
|
-
'use client'
|
|
258
|
-
|
|
259
|
-
import { useState, useCallback } from 'react'
|
|
260
|
-
import { Search, X } from 'lucide-react'
|
|
261
|
-
import { useDebouncedCallback } from 'use-debounce'
|
|
262
|
-
|
|
263
|
-
interface Props {
|
|
264
|
-
placeholder?: string
|
|
265
|
-
onSearch: (query: string) => void
|
|
266
|
-
debounceMs?: number
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
export function SearchInput({
|
|
270
|
-
placeholder = 'Search...',
|
|
271
|
-
onSearch,
|
|
272
|
-
debounceMs = 300
|
|
273
|
-
}: Props) {
|
|
274
|
-
const [value, setValue] = useState('')
|
|
275
|
-
|
|
276
|
-
const debouncedSearch = useDebouncedCallback(onSearch, debounceMs)
|
|
277
|
-
|
|
278
|
-
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
279
|
-
const newValue = e.target.value
|
|
280
|
-
setValue(newValue)
|
|
281
|
-
debouncedSearch(newValue)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const handleClear = () => {
|
|
285
|
-
setValue('')
|
|
286
|
-
onSearch('')
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return (
|
|
290
|
-
<div className="relative">
|
|
291
|
-
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
|
|
292
|
-
|
|
293
|
-
<input
|
|
294
|
-
type="text"
|
|
295
|
-
value={value}
|
|
296
|
-
onChange={handleChange}
|
|
297
|
-
placeholder={placeholder}
|
|
298
|
-
className="w-full rounded-lg border border-gray-300 py-2 pl-10 pr-10 outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500"
|
|
299
|
-
/>
|
|
300
|
-
|
|
301
|
-
{value && (
|
|
302
|
-
<button
|
|
303
|
-
onClick={handleClear}
|
|
304
|
-
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
|
305
|
-
>
|
|
306
|
-
<X className="h-4 w-4" />
|
|
307
|
-
</button>
|
|
308
|
-
)}
|
|
309
|
-
</div>
|
|
310
|
-
)
|
|
311
|
-
}
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
## When to Use
|
|
315
|
-
|
|
316
|
-
- Form data collection
|
|
317
|
-
- User preferences
|
|
318
|
-
- Search interfaces
|
|
319
|
-
- Settings pages
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
# Layout Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for building responsive layouts.
|
|
4
|
-
|
|
5
|
-
## App Shell Layout
|
|
6
|
-
|
|
7
|
-
```tsx
|
|
8
|
-
// components/layout/AppShell.tsx
|
|
9
|
-
import { Header } from './Header'
|
|
10
|
-
import { Sidebar } from './Sidebar'
|
|
11
|
-
|
|
12
|
-
export function AppShell({ children }: { children: React.ReactNode }) {
|
|
13
|
-
return (
|
|
14
|
-
<div className="flex min-h-screen flex-col">
|
|
15
|
-
<Header />
|
|
16
|
-
<div className="flex flex-1">
|
|
17
|
-
<Sidebar />
|
|
18
|
-
<main className="flex-1 p-6">{children}</main>
|
|
19
|
-
</div>
|
|
20
|
-
</div>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// app/dashboard/layout.tsx
|
|
25
|
-
export default function DashboardLayout({
|
|
26
|
-
children
|
|
27
|
-
}: {
|
|
28
|
-
children: React.ReactNode
|
|
29
|
-
}) {
|
|
30
|
-
return <AppShell>{children}</AppShell>
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Collapsible Sidebar
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
// components/layout/CollapsibleSidebar.tsx
|
|
38
|
-
'use client'
|
|
39
|
-
|
|
40
|
-
import { useState } from 'react'
|
|
41
|
-
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
|
42
|
-
|
|
43
|
-
export function CollapsibleSidebar({ children }: { children: React.ReactNode }) {
|
|
44
|
-
const [collapsed, setCollapsed] = useState(false)
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<aside
|
|
48
|
-
className={`relative border-r bg-gray-50 transition-all duration-300 ${
|
|
49
|
-
collapsed ? 'w-16' : 'w-64'
|
|
50
|
-
}`}
|
|
51
|
-
>
|
|
52
|
-
<button
|
|
53
|
-
onClick={() => setCollapsed(!collapsed)}
|
|
54
|
-
className="absolute -right-3 top-6 z-10 rounded-full border bg-white p-1 shadow-sm"
|
|
55
|
-
>
|
|
56
|
-
{collapsed ? (
|
|
57
|
-
<ChevronRight className="h-4 w-4" />
|
|
58
|
-
) : (
|
|
59
|
-
<ChevronLeft className="h-4 w-4" />
|
|
60
|
-
)}
|
|
61
|
-
</button>
|
|
62
|
-
|
|
63
|
-
<div className={collapsed ? 'overflow-hidden' : ''}>
|
|
64
|
-
{children}
|
|
65
|
-
</div>
|
|
66
|
-
</aside>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Split Layout
|
|
72
|
-
|
|
73
|
-
```tsx
|
|
74
|
-
// components/layout/SplitLayout.tsx
|
|
75
|
-
interface Props {
|
|
76
|
-
left: React.ReactNode
|
|
77
|
-
right: React.ReactNode
|
|
78
|
-
leftWidth?: string
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function SplitLayout({
|
|
82
|
-
left,
|
|
83
|
-
right,
|
|
84
|
-
leftWidth = 'w-1/3'
|
|
85
|
-
}: Props) {
|
|
86
|
-
return (
|
|
87
|
-
<div className="flex min-h-screen">
|
|
88
|
-
<div className={`${leftWidth} border-r bg-gray-50 p-6`}>
|
|
89
|
-
{left}
|
|
90
|
-
</div>
|
|
91
|
-
<div className="flex-1 p-6">{right}</div>
|
|
92
|
-
</div>
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Usage - Email app layout
|
|
97
|
-
<SplitLayout
|
|
98
|
-
left={<EmailList emails={emails} />}
|
|
99
|
-
right={<EmailDetail email={selectedEmail} />}
|
|
100
|
-
/>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Container
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
// components/ui/Container.tsx
|
|
107
|
-
interface Props {
|
|
108
|
-
children: React.ReactNode
|
|
109
|
-
size?: 'sm' | 'md' | 'lg' | 'xl' | 'full'
|
|
110
|
-
className?: string
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const sizeClasses = {
|
|
114
|
-
sm: 'max-w-2xl',
|
|
115
|
-
md: 'max-w-4xl',
|
|
116
|
-
lg: 'max-w-6xl',
|
|
117
|
-
xl: 'max-w-7xl',
|
|
118
|
-
full: 'max-w-full'
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function Container({ children, size = 'xl', className = '' }: Props) {
|
|
122
|
-
return (
|
|
123
|
-
<div className={`mx-auto px-4 ${sizeClasses[size]} ${className}`}>
|
|
124
|
-
{children}
|
|
125
|
-
</div>
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Grid Layouts
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
// components/layout/Grid.tsx
|
|
134
|
-
interface Props {
|
|
135
|
-
children: React.ReactNode
|
|
136
|
-
cols?: 1 | 2 | 3 | 4 | 6
|
|
137
|
-
gap?: 'sm' | 'md' | 'lg'
|
|
138
|
-
className?: string
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function Grid({ children, cols = 3, gap = 'md', className = '' }: Props) {
|
|
142
|
-
const colClasses = {
|
|
143
|
-
1: 'grid-cols-1',
|
|
144
|
-
2: 'grid-cols-1 md:grid-cols-2',
|
|
145
|
-
3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
|
146
|
-
4: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-4',
|
|
147
|
-
6: 'grid-cols-2 md:grid-cols-3 lg:grid-cols-6'
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const gapClasses = {
|
|
151
|
-
sm: 'gap-2',
|
|
152
|
-
md: 'gap-4',
|
|
153
|
-
lg: 'gap-6'
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return (
|
|
157
|
-
<div className={`grid ${colClasses[cols]} ${gapClasses[gap]} ${className}`}>
|
|
158
|
-
{children}
|
|
159
|
-
</div>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Usage
|
|
164
|
-
<Grid cols={3} gap="lg">
|
|
165
|
-
<Card>...</Card>
|
|
166
|
-
<Card>...</Card>
|
|
167
|
-
<Card>...</Card>
|
|
168
|
-
</Grid>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Stack Layout
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
// components/ui/Stack.tsx
|
|
175
|
-
interface Props {
|
|
176
|
-
children: React.ReactNode
|
|
177
|
-
direction?: 'vertical' | 'horizontal'
|
|
178
|
-
gap?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
179
|
-
align?: 'start' | 'center' | 'end' | 'stretch'
|
|
180
|
-
justify?: 'start' | 'center' | 'end' | 'between' | 'around'
|
|
181
|
-
className?: string
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export function Stack({
|
|
185
|
-
children,
|
|
186
|
-
direction = 'vertical',
|
|
187
|
-
gap = 'md',
|
|
188
|
-
align = 'stretch',
|
|
189
|
-
justify = 'start',
|
|
190
|
-
className = ''
|
|
191
|
-
}: Props) {
|
|
192
|
-
const gapClasses = { xs: 'gap-1', sm: 'gap-2', md: 'gap-4', lg: 'gap-6', xl: 'gap-8' }
|
|
193
|
-
const alignClasses = { start: 'items-start', center: 'items-center', end: 'items-end', stretch: 'items-stretch' }
|
|
194
|
-
const justifyClasses = { start: 'justify-start', center: 'justify-center', end: 'justify-end', between: 'justify-between', around: 'justify-around' }
|
|
195
|
-
|
|
196
|
-
return (
|
|
197
|
-
<div
|
|
198
|
-
className={`flex ${
|
|
199
|
-
direction === 'vertical' ? 'flex-col' : 'flex-row'
|
|
200
|
-
} ${gapClasses[gap]} ${alignClasses[align]} ${justifyClasses[justify]} ${className}`}
|
|
201
|
-
>
|
|
202
|
-
{children}
|
|
203
|
-
</div>
|
|
204
|
-
)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Usage
|
|
208
|
-
<Stack direction="horizontal" gap="lg" align="center" justify="between">
|
|
209
|
-
<Logo />
|
|
210
|
-
<Nav />
|
|
211
|
-
<UserMenu />
|
|
212
|
-
</Stack>
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
## Responsive Layout
|
|
216
|
-
|
|
217
|
-
```tsx
|
|
218
|
-
// components/layout/ResponsiveLayout.tsx
|
|
219
|
-
interface Props {
|
|
220
|
-
sidebar: React.ReactNode
|
|
221
|
-
main: React.ReactNode
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function ResponsiveLayout({ sidebar, main }: Props) {
|
|
225
|
-
return (
|
|
226
|
-
<div className="lg:flex">
|
|
227
|
-
{/* Mobile: Full width, hidden on desktop */}
|
|
228
|
-
<div className="lg:hidden">
|
|
229
|
-
<MobileHeader />
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
{/* Desktop: Fixed sidebar */}
|
|
233
|
-
<aside className="hidden lg:block lg:w-64 lg:shrink-0">
|
|
234
|
-
<div className="sticky top-0 h-screen overflow-y-auto border-r p-4">
|
|
235
|
-
{sidebar}
|
|
236
|
-
</div>
|
|
237
|
-
</aside>
|
|
238
|
-
|
|
239
|
-
{/* Main content */}
|
|
240
|
-
<main className="flex-1 p-4 lg:p-8">
|
|
241
|
-
{main}
|
|
242
|
-
</main>
|
|
243
|
-
</div>
|
|
244
|
-
)
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## Centered Layout
|
|
249
|
-
|
|
250
|
-
```tsx
|
|
251
|
-
// components/layout/CenteredLayout.tsx
|
|
252
|
-
export function CenteredLayout({ children }: { children: React.ReactNode }) {
|
|
253
|
-
return (
|
|
254
|
-
<div className="flex min-h-screen items-center justify-center bg-gray-50 p-4">
|
|
255
|
-
<div className="w-full max-w-md">{children}</div>
|
|
256
|
-
</div>
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Usage - Login page
|
|
261
|
-
export default function LoginPage() {
|
|
262
|
-
return (
|
|
263
|
-
<CenteredLayout>
|
|
264
|
-
<Card>
|
|
265
|
-
<CardHeader>
|
|
266
|
-
<CardTitle>Sign In</CardTitle>
|
|
267
|
-
</CardHeader>
|
|
268
|
-
<CardContent>
|
|
269
|
-
<LoginForm />
|
|
270
|
-
</CardContent>
|
|
271
|
-
</Card>
|
|
272
|
-
</CenteredLayout>
|
|
273
|
-
)
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
## When to Use
|
|
278
|
-
|
|
279
|
-
- App structure
|
|
280
|
-
- Dashboard layouts
|
|
281
|
-
- Responsive design
|
|
282
|
-
- Content organization
|