@nextsparkjs/theme-default 0.1.0-beta.1
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/about/business.md +49 -0
- package/about/features.json +302 -0
- package/about/team.md +79 -0
- package/api/ai/chat/stream/route.ts +212 -0
- package/api/ai/orchestrator/route.ts +226 -0
- package/api/ai/single-agent/route.ts +291 -0
- package/api/ai/usage/route.ts +122 -0
- package/blocks/benefits/component.tsx +100 -0
- package/blocks/benefits/config.ts +11 -0
- package/blocks/benefits/examples.ts +85 -0
- package/blocks/benefits/fields.ts +156 -0
- package/blocks/benefits/schema.ts +33 -0
- package/blocks/cta-section/component.tsx +100 -0
- package/blocks/cta-section/config.ts +11 -0
- package/blocks/cta-section/examples.ts +41 -0
- package/blocks/cta-section/fields.ts +89 -0
- package/blocks/cta-section/index.ts +6 -0
- package/blocks/cta-section/schema.ts +32 -0
- package/blocks/cta-section/thumbnail.png +1 -0
- package/blocks/faq-accordion/component.tsx +156 -0
- package/blocks/faq-accordion/config.ts +11 -0
- package/blocks/faq-accordion/examples.ts +77 -0
- package/blocks/faq-accordion/fields.ts +119 -0
- package/blocks/faq-accordion/index.ts +6 -0
- package/blocks/faq-accordion/schema.ts +45 -0
- package/blocks/features-grid/component.tsx +112 -0
- package/blocks/features-grid/config.ts +11 -0
- package/blocks/features-grid/examples.ts +63 -0
- package/blocks/features-grid/fields.ts +97 -0
- package/blocks/features-grid/index.ts +6 -0
- package/blocks/features-grid/schema.ts +40 -0
- package/blocks/features-grid/thumbnail.png +1 -0
- package/blocks/hero/component.tsx +100 -0
- package/blocks/hero/config.ts +11 -0
- package/blocks/hero/examples.ts +35 -0
- package/blocks/hero/fields.ts +60 -0
- package/blocks/hero/index.ts +6 -0
- package/blocks/hero/schema.ts +32 -0
- package/blocks/hero/thumbnail.png +1 -0
- package/blocks/hero/thumbnail.png.txt +6 -0
- package/blocks/hero-with-form/component.tsx +232 -0
- package/blocks/hero-with-form/config.ts +11 -0
- package/blocks/hero-with-form/examples.ts +16 -0
- package/blocks/hero-with-form/fields.ts +207 -0
- package/blocks/hero-with-form/index.ts +6 -0
- package/blocks/hero-with-form/schema.ts +54 -0
- package/blocks/jumbotron/component.tsx +136 -0
- package/blocks/jumbotron/config.ts +11 -0
- package/blocks/jumbotron/examples.ts +36 -0
- package/blocks/jumbotron/fields.ts +202 -0
- package/blocks/jumbotron/index.ts +6 -0
- package/blocks/jumbotron/schema.ts +55 -0
- package/blocks/logo-cloud/component.tsx +154 -0
- package/blocks/logo-cloud/config.ts +11 -0
- package/blocks/logo-cloud/examples.ts +34 -0
- package/blocks/logo-cloud/fields.ts +133 -0
- package/blocks/logo-cloud/index.ts +6 -0
- package/blocks/logo-cloud/schema.ts +46 -0
- package/blocks/post-content/component.tsx +197 -0
- package/blocks/post-content/config.ts +11 -0
- package/blocks/post-content/examples.ts +33 -0
- package/blocks/post-content/fields.ts +165 -0
- package/blocks/post-content/index.ts +4 -0
- package/blocks/post-content/schema.ts +46 -0
- package/blocks/pricing-table/component.tsx +154 -0
- package/blocks/pricing-table/config.ts +11 -0
- package/blocks/pricing-table/examples.ts +96 -0
- package/blocks/pricing-table/fields.ts +161 -0
- package/blocks/pricing-table/index.ts +4 -0
- package/blocks/pricing-table/schema.ts +50 -0
- package/blocks/split-content/component.tsx +135 -0
- package/blocks/split-content/config.ts +11 -0
- package/blocks/split-content/examples.ts +38 -0
- package/blocks/split-content/fields.ts +198 -0
- package/blocks/split-content/index.ts +6 -0
- package/blocks/split-content/schema.ts +67 -0
- package/blocks/stats-counter/component.tsx +124 -0
- package/blocks/stats-counter/config.ts +11 -0
- package/blocks/stats-counter/examples.ts +61 -0
- package/blocks/stats-counter/fields.ts +134 -0
- package/blocks/stats-counter/index.ts +6 -0
- package/blocks/stats-counter/schema.ts +47 -0
- package/blocks/testimonials/component.tsx +114 -0
- package/blocks/testimonials/config.ts +11 -0
- package/blocks/testimonials/examples.ts +65 -0
- package/blocks/testimonials/fields.ts +105 -0
- package/blocks/testimonials/index.ts +6 -0
- package/blocks/testimonials/schema.ts +41 -0
- package/blocks/testimonials/thumbnail.png +1 -0
- package/blocks/text-content/component.tsx +97 -0
- package/blocks/text-content/config.ts +11 -0
- package/blocks/text-content/examples.ts +30 -0
- package/blocks/text-content/fields.ts +88 -0
- package/blocks/text-content/index.ts +6 -0
- package/blocks/text-content/schema.ts +30 -0
- package/blocks/text-content/thumbnail.png +1 -0
- package/blocks/timeline/component.tsx +267 -0
- package/blocks/timeline/config.ts +11 -0
- package/blocks/timeline/examples.ts +68 -0
- package/blocks/timeline/fields.ts +147 -0
- package/blocks/timeline/index.ts +6 -0
- package/blocks/timeline/schema.ts +49 -0
- package/blocks/video-hero/component.tsx +270 -0
- package/blocks/video-hero/config.ts +11 -0
- package/blocks/video-hero/examples.ts +24 -0
- package/blocks/video-hero/fields.ts +98 -0
- package/blocks/video-hero/index.ts +6 -0
- package/blocks/video-hero/schema.ts +39 -0
- package/components/ai-chat/ChatPanel.tsx +575 -0
- package/components/ai-chat/ConversationItem.tsx +266 -0
- package/components/ai-chat/ConversationSidebar.tsx +99 -0
- package/components/ai-chat/MarkdownRenderer.tsx +15 -0
- package/components/ai-chat/Message.tsx +42 -0
- package/components/ai-chat/MessageInput.tsx +49 -0
- package/components/ai-chat/MessageList.tsx +46 -0
- package/components/ai-chat/TypingIndicator.tsx +11 -0
- package/config/app.config.ts +367 -0
- package/config/billing.config.ts +349 -0
- package/config/dashboard.config.ts +506 -0
- package/config/dev.config.ts +104 -0
- package/config/features.config.ts +203 -0
- package/config/flows.config.ts +129 -0
- package/config/permissions.config.ts +245 -0
- package/config/theme.config.ts +74 -0
- package/docs/01-overview/01-introduction.md +335 -0
- package/docs/01-overview/02-customization.md +671 -0
- package/docs/02-features/01-components.md +155 -0
- package/docs/02-features/02-styling.md +139 -0
- package/docs/02-features/03-tasks-entity.md +407 -0
- package/docs/03-ai/01-overview.md +211 -0
- package/docs/03-ai/02-customization.md +436 -0
- package/entities/customers/customers.config.ts +75 -0
- package/entities/customers/customers.fields.ts +165 -0
- package/entities/customers/customers.service.ts +516 -0
- package/entities/customers/customers.types.ts +83 -0
- package/entities/customers/messages/en.json +66 -0
- package/entities/customers/messages/es.json +66 -0
- package/entities/customers/migrations/001_customers_table.sql +102 -0
- package/entities/customers/migrations/002_customers_metas.sql +92 -0
- package/entities/pages/messages/en.json +41 -0
- package/entities/pages/messages/es.json +41 -0
- package/entities/pages/migrations/001_pages_table.sql +112 -0
- package/entities/pages/migrations/002_pages_metas.sql +56 -0
- package/entities/pages/migrations/003_add_status.sql +50 -0
- package/entities/pages/pages-management.service.ts +610 -0
- package/entities/pages/pages.config.ts +94 -0
- package/entities/pages/pages.fields.ts +101 -0
- package/entities/pages/pages.service.ts +290 -0
- package/entities/pages/pages.types.ts +124 -0
- package/entities/posts/components/post-header.tsx +97 -0
- package/entities/posts/messages/en.json +55 -0
- package/entities/posts/messages/es.json +55 -0
- package/entities/posts/migrations/001_posts_table.sql +115 -0
- package/entities/posts/migrations/003_add_status.sql +44 -0
- package/entities/posts/migrations/004_entity_taxonomy_relations.sql +129 -0
- package/entities/posts/migrations/006_posts_metas.sql +56 -0
- package/entities/posts/posts.config.ts +101 -0
- package/entities/posts/posts.fields.ts +116 -0
- package/entities/posts/posts.service.ts +376 -0
- package/entities/posts/posts.types.ts +74 -0
- package/entities/tasks/messages/en.json +204 -0
- package/entities/tasks/messages/es.json +204 -0
- package/entities/tasks/migrations/001_tasks_table.sql +105 -0
- package/entities/tasks/migrations/002_task_metas.sql +85 -0
- package/entities/tasks/migrations/sample_data.json +77 -0
- package/entities/tasks/tasks.config.ts +79 -0
- package/entities/tasks/tasks.fields.ts +196 -0
- package/entities/tasks/tasks.service.ts +541 -0
- package/entities/tasks/tasks.types.ts +56 -0
- package/lib/hooks/useAiChat.ts +114 -0
- package/lib/hooks/useConversations.ts +376 -0
- package/lib/hooks/useOrchestratorChat.ts +122 -0
- package/lib/hooks/usePersistentChat.ts +315 -0
- package/lib/hooks/useStreamingChat.ts +127 -0
- package/lib/hooks/useTokenUsage.ts +63 -0
- package/lib/langchain/agents/customer-assistant.md +69 -0
- package/lib/langchain/agents/index.ts +61 -0
- package/lib/langchain/agents/orchestrator.md +59 -0
- package/lib/langchain/agents/page-assistant.md +85 -0
- package/lib/langchain/agents/single-agent.md +46 -0
- package/lib/langchain/agents/task-assistant.md +55 -0
- package/lib/langchain/config.ts +45 -0
- package/lib/langchain/handlers/customer-handler.ts +338 -0
- package/lib/langchain/handlers/page-handler.ts +232 -0
- package/lib/langchain/handlers/task-handler.ts +323 -0
- package/lib/langchain/langchain.config.ts +223 -0
- package/lib/langchain/observability.config.ts +30 -0
- package/lib/langchain/orchestrator.ts +562 -0
- package/lib/langchain/tools/customers.ts +176 -0
- package/lib/langchain/tools/index.ts +10 -0
- package/lib/langchain/tools/orchestrator.ts +92 -0
- package/lib/langchain/tools/pages.ts +289 -0
- package/lib/langchain/tools/tasks.ts +167 -0
- package/lib/scheduled-actions/billing.ts +149 -0
- package/lib/scheduled-actions/index.ts +170 -0
- package/lib/scheduled-actions/webhook.ts +231 -0
- package/lib/selectors.ts +197 -0
- package/messages/de/admin.json +219 -0
- package/messages/de/aiUsage.json +36 -0
- package/messages/de/buttons.json +19 -0
- package/messages/de/categories.json +35 -0
- package/messages/de/common.json +16 -0
- package/messages/de/dev.json +101 -0
- package/messages/de/docs.json +27 -0
- package/messages/de/entities.json +7 -0
- package/messages/de/features.json +119 -0
- package/messages/de/footer.json +22 -0
- package/messages/de/home.json +57 -0
- package/messages/de/index.ts +39 -0
- package/messages/de/mobileNav.json +13 -0
- package/messages/de/navigation.json +8 -0
- package/messages/de/observability.json +74 -0
- package/messages/de/posts.json +54 -0
- package/messages/de/pricing.json +102 -0
- package/messages/de/support.json +9 -0
- package/messages/de/teams.json +8 -0
- package/messages/en/admin.json +219 -0
- package/messages/en/aiUsage.json +36 -0
- package/messages/en/buttons.json +19 -0
- package/messages/en/categories.json +35 -0
- package/messages/en/common.json +16 -0
- package/messages/en/dev.json +106 -0
- package/messages/en/docs.json +27 -0
- package/messages/en/entities.json +7 -0
- package/messages/en/features.json +119 -0
- package/messages/en/footer.json +22 -0
- package/messages/en/home.json +57 -0
- package/messages/en/index.ts +39 -0
- package/messages/en/mobileNav.json +13 -0
- package/messages/en/navigation.json +8 -0
- package/messages/en/observability.json +74 -0
- package/messages/en/posts.json +54 -0
- package/messages/en/pricing.json +102 -0
- package/messages/en/support.json +9 -0
- package/messages/en/teams.json +8 -0
- package/messages/es/admin.json +219 -0
- package/messages/es/aiUsage.json +36 -0
- package/messages/es/buttons.json +19 -0
- package/messages/es/categories.json +35 -0
- package/messages/es/common.json +16 -0
- package/messages/es/dev.json +101 -0
- package/messages/es/docs.json +27 -0
- package/messages/es/entities.json +7 -0
- package/messages/es/features.json +119 -0
- package/messages/es/footer.json +22 -0
- package/messages/es/home.json +57 -0
- package/messages/es/index.ts +39 -0
- package/messages/es/mobileNav.json +13 -0
- package/messages/es/navigation.json +8 -0
- package/messages/es/observability.json +74 -0
- package/messages/es/posts.json +54 -0
- package/messages/es/pricing.json +102 -0
- package/messages/es/support.json +9 -0
- package/messages/es/teams.json +8 -0
- package/messages/fr/admin.json +219 -0
- package/messages/fr/aiUsage.json +36 -0
- package/messages/fr/buttons.json +19 -0
- package/messages/fr/categories.json +35 -0
- package/messages/fr/common.json +16 -0
- package/messages/fr/dev.json +101 -0
- package/messages/fr/docs.json +27 -0
- package/messages/fr/entities.json +7 -0
- package/messages/fr/features.json +119 -0
- package/messages/fr/footer.json +22 -0
- package/messages/fr/home.json +57 -0
- package/messages/fr/index.ts +39 -0
- package/messages/fr/mobileNav.json +13 -0
- package/messages/fr/navigation.json +8 -0
- package/messages/fr/observability.json +74 -0
- package/messages/fr/posts.json +54 -0
- package/messages/fr/pricing.json +102 -0
- package/messages/fr/support.json +9 -0
- package/messages/fr/teams.json +8 -0
- package/messages/it/admin.json +219 -0
- package/messages/it/aiUsage.json +36 -0
- package/messages/it/buttons.json +19 -0
- package/messages/it/categories.json +35 -0
- package/messages/it/common.json +16 -0
- package/messages/it/dev.json +101 -0
- package/messages/it/docs.json +27 -0
- package/messages/it/entities.json +7 -0
- package/messages/it/features.json +119 -0
- package/messages/it/footer.json +22 -0
- package/messages/it/home.json +57 -0
- package/messages/it/index.ts +39 -0
- package/messages/it/mobileNav.json +13 -0
- package/messages/it/navigation.json +8 -0
- package/messages/it/observability.json +74 -0
- package/messages/it/posts.json +54 -0
- package/messages/it/pricing.json +102 -0
- package/messages/it/support.json +9 -0
- package/messages/it/teams.json +8 -0
- package/messages/pt/admin.json +219 -0
- package/messages/pt/aiUsage.json +36 -0
- package/messages/pt/buttons.json +19 -0
- package/messages/pt/categories.json +35 -0
- package/messages/pt/common.json +16 -0
- package/messages/pt/dev.json +101 -0
- package/messages/pt/docs.json +27 -0
- package/messages/pt/entities.json +7 -0
- package/messages/pt/features.json +119 -0
- package/messages/pt/footer.json +22 -0
- package/messages/pt/home.json +57 -0
- package/messages/pt/index.ts +39 -0
- package/messages/pt/mobileNav.json +13 -0
- package/messages/pt/navigation.json +8 -0
- package/messages/pt/observability.json +74 -0
- package/messages/pt/posts.json +54 -0
- package/messages/pt/pricing.json +102 -0
- package/messages/pt/support.json +9 -0
- package/messages/pt/teams.json +8 -0
- package/migrations/089_add_editor_team_role.sql +39 -0
- package/migrations/090_demo_users_teams.sql +540 -0
- package/migrations/091_greek_teams_billing.sql +523 -0
- package/migrations/092_billing_sample_data.sql +774 -0
- package/migrations/093_pages_sample_data.sql +1158 -0
- package/migrations/094_posts_sample_data.sql +278 -0
- package/migrations/095_tasks_sample_data.sql +440 -0
- package/migrations/096_customers_sample_data.sql +358 -0
- package/migrations/097_scheduled_actions_sample_data.sql +111 -0
- package/package.json +22 -0
- package/public/docs/desktop-layout-example.png +0 -0
- package/styles/components.css +11 -0
- package/styles/globals.css +179 -0
- package/templates/(public)/blog/[slug]/page.tsx +65 -0
- package/templates/(public)/layout.tsx +25 -0
- package/templates/(public)/page.tsx +200 -0
- package/templates/(public)/support/page.tsx +321 -0
- package/templates/dashboard/(main)/agent-multi/page.tsx +63 -0
- package/templates/dashboard/(main)/agent-single/page.tsx +142 -0
- package/templates/dashboard/(main)/settings/ai-usage/page.tsx +157 -0
- package/templates/superadmin/ai-observability/[traceId]/page.tsx +27 -0
- package/templates/superadmin/ai-observability/page.tsx +17 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { baseBlockSchema, ctaSchema } from '@nextsparkjs/core/types/blocks'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Primary CTA Schema for Jumbotron
|
|
6
|
+
* Extends standard CTA with variant options
|
|
7
|
+
*/
|
|
8
|
+
const primaryCtaSchema = ctaSchema.extend({
|
|
9
|
+
variant: z.enum(['default', 'outline', 'secondary']).default('default'),
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Secondary CTA Schema for Jumbotron
|
|
14
|
+
* Uses same structure as primary CTA
|
|
15
|
+
*/
|
|
16
|
+
const secondaryCtaSchema = ctaSchema.extend({
|
|
17
|
+
variant: z.enum(['default', 'outline', 'secondary']).default('outline'),
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export type PrimaryCta = z.infer<typeof primaryCtaSchema>
|
|
21
|
+
export type SecondaryCta = z.infer<typeof secondaryCtaSchema>
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Jumbotron Block Schema
|
|
25
|
+
*
|
|
26
|
+
* Extends base schema with:
|
|
27
|
+
* - subtitle: Supporting text/description (separate from content)
|
|
28
|
+
* - primaryCta: Primary CTA button with variant
|
|
29
|
+
* - secondaryCta: Optional secondary CTA button with variant
|
|
30
|
+
* - fullscreen: Toggle for 100vh height
|
|
31
|
+
* - backgroundImage: Optional background image
|
|
32
|
+
* - textColor: Light/dark for contrast
|
|
33
|
+
* - textAlign: Content alignment (center, left, right)
|
|
34
|
+
*
|
|
35
|
+
* Note: Uses base schema title, backgroundColor, className, id
|
|
36
|
+
*/
|
|
37
|
+
export const jumbotronSpecificSchema = z.object({
|
|
38
|
+
// Content fields
|
|
39
|
+
subtitle: z.string().optional(),
|
|
40
|
+
primaryCta: primaryCtaSchema.optional(),
|
|
41
|
+
secondaryCta: secondaryCtaSchema.optional(),
|
|
42
|
+
|
|
43
|
+
// Design fields
|
|
44
|
+
fullscreen: z.boolean().default(false),
|
|
45
|
+
backgroundImage: z.string().url('Must be a valid URL').optional(),
|
|
46
|
+
textColor: z.enum(['light', 'dark']).default('light'),
|
|
47
|
+
textAlign: z.enum(['center', 'left', 'right']).default('center'),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Complete Jumbotron Block Schema
|
|
52
|
+
*/
|
|
53
|
+
export const schema = baseBlockSchema.merge(jumbotronSpecificSchema)
|
|
54
|
+
|
|
55
|
+
export type JumbotronBlockProps = z.infer<typeof schema>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Image from 'next/image'
|
|
3
|
+
import { cn } from '@nextsparkjs/core/lib/utils'
|
|
4
|
+
import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
|
|
5
|
+
import { sel } from '../../lib/selectors'
|
|
6
|
+
import type { LogoCloudBlockProps, LogoItem } from './schema'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Logo Cloud Block Component
|
|
10
|
+
*
|
|
11
|
+
* Props from 3-tab structure:
|
|
12
|
+
* - Content: title, content, cta, logos
|
|
13
|
+
* - Design: backgroundColor, layout, columns, grayscale, size
|
|
14
|
+
* - Advanced: className, id
|
|
15
|
+
*/
|
|
16
|
+
export function LogoCloudBlock({
|
|
17
|
+
// Base content props
|
|
18
|
+
title,
|
|
19
|
+
content,
|
|
20
|
+
cta,
|
|
21
|
+
// Logo Cloud-specific content
|
|
22
|
+
logos,
|
|
23
|
+
// Base design props
|
|
24
|
+
backgroundColor,
|
|
25
|
+
// Logo Cloud-specific design
|
|
26
|
+
layout = 'grid',
|
|
27
|
+
columns = '5',
|
|
28
|
+
grayscale = true,
|
|
29
|
+
size = 'md',
|
|
30
|
+
// Base advanced props
|
|
31
|
+
className,
|
|
32
|
+
id,
|
|
33
|
+
}: LogoCloudBlockProps) {
|
|
34
|
+
// Ensure logos is an array
|
|
35
|
+
const safeLogos = Array.isArray(logos) ? logos : []
|
|
36
|
+
|
|
37
|
+
// Build layout classes based on layout prop
|
|
38
|
+
const layoutClasses: Record<string, string> = {
|
|
39
|
+
grid: 'grid',
|
|
40
|
+
row: 'flex flex-wrap',
|
|
41
|
+
'row-scroll': 'flex overflow-x-auto',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Build column classes for grid layout
|
|
45
|
+
const columnClasses: Record<string, string> = {
|
|
46
|
+
'3': 'grid-cols-2 sm:grid-cols-3',
|
|
47
|
+
'4': 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4',
|
|
48
|
+
'5': 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5',
|
|
49
|
+
'6': 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build size classes for logo containers
|
|
53
|
+
const sizeClasses: Record<string, string> = {
|
|
54
|
+
sm: 'h-12 w-24',
|
|
55
|
+
md: 'h-16 w-32',
|
|
56
|
+
lg: 'h-20 w-40',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Container classes based on layout
|
|
60
|
+
const containerClasses = cn(
|
|
61
|
+
layoutClasses[layout],
|
|
62
|
+
layout === 'grid' && cn('gap-8', columnClasses[columns]),
|
|
63
|
+
layout === 'row' && 'gap-6 justify-center items-center',
|
|
64
|
+
layout === 'row-scroll' && 'gap-6 overflow-x-auto pb-4',
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
// Build section classes with background and custom className
|
|
68
|
+
const sectionClasses = buildSectionClasses(
|
|
69
|
+
'py-12 px-4 md:py-16',
|
|
70
|
+
{ backgroundColor, className }
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<section id={id} className={sectionClasses} data-cy={sel('blocks.logoCloud.container')}>
|
|
75
|
+
<div className="container mx-auto max-w-6xl">
|
|
76
|
+
{/* Section Header */}
|
|
77
|
+
{(title || content) && (
|
|
78
|
+
<div className="mb-10 text-center">
|
|
79
|
+
{title && (
|
|
80
|
+
<h2 className="mb-3 text-2xl font-semibold md:text-3xl">
|
|
81
|
+
{title}
|
|
82
|
+
</h2>
|
|
83
|
+
)}
|
|
84
|
+
{content && (
|
|
85
|
+
<p className="mx-auto max-w-2xl text-muted-foreground">
|
|
86
|
+
{content}
|
|
87
|
+
</p>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{/* Logos Container */}
|
|
93
|
+
<div className={containerClasses}>
|
|
94
|
+
{safeLogos.map((logo: LogoItem, index: number) => {
|
|
95
|
+
const LogoImage = (
|
|
96
|
+
<div
|
|
97
|
+
className={cn(
|
|
98
|
+
'relative flex items-center justify-center transition-all duration-300',
|
|
99
|
+
sizeClasses[size],
|
|
100
|
+
grayscale && 'grayscale hover:grayscale-0 opacity-70 hover:opacity-100'
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
<Image
|
|
104
|
+
src={logo.image}
|
|
105
|
+
alt={logo.alt}
|
|
106
|
+
fill
|
|
107
|
+
className="object-contain"
|
|
108
|
+
sizes="(max-width: 768px) 50vw, (max-width: 1200px) 33vw, 20vw"
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<div
|
|
115
|
+
key={index}
|
|
116
|
+
className="flex items-center justify-center"
|
|
117
|
+
>
|
|
118
|
+
{logo.url ? (
|
|
119
|
+
<a
|
|
120
|
+
href={logo.url}
|
|
121
|
+
target="_blank"
|
|
122
|
+
rel="noopener noreferrer"
|
|
123
|
+
className="block"
|
|
124
|
+
data-cy={sel('blocks.logoCloud.link', { index: String(index) })}
|
|
125
|
+
>
|
|
126
|
+
{LogoImage}
|
|
127
|
+
</a>
|
|
128
|
+
) : (
|
|
129
|
+
<div data-cy={sel('blocks.logoCloud.item', { index: String(index) })}>
|
|
130
|
+
{LogoImage}
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
)
|
|
135
|
+
})}
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{/* Optional CTA */}
|
|
139
|
+
{cta && (
|
|
140
|
+
<div className="mt-10 text-center">
|
|
141
|
+
<a
|
|
142
|
+
href={cta.link}
|
|
143
|
+
target={cta.target}
|
|
144
|
+
rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
145
|
+
className="inline-flex items-center justify-center rounded-md bg-primary px-6 py-2.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
146
|
+
>
|
|
147
|
+
{cta.text}
|
|
148
|
+
</a>
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
</section>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
|
|
2
|
+
|
|
3
|
+
export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
|
|
4
|
+
slug: 'logo-cloud',
|
|
5
|
+
name: 'Logo Cloud',
|
|
6
|
+
description: 'A section displaying client/partner logos in a grid or row layout ("Trusted by...", "As seen in...")',
|
|
7
|
+
category: 'content',
|
|
8
|
+
icon: 'Building2',
|
|
9
|
+
thumbnail: '/theme/blocks/logo-cloud/thumbnail.png',
|
|
10
|
+
scope: ['pages']
|
|
11
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BlockExample } from '@nextsparkjs/core/types/blocks'
|
|
2
|
+
|
|
3
|
+
export const examples: BlockExample[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Default',
|
|
6
|
+
description: 'Logo cloud showing trusted brands',
|
|
7
|
+
props: {
|
|
8
|
+
title: 'Trusted by Leading Companies',
|
|
9
|
+
backgroundColor: 'gray-50',
|
|
10
|
+
logos: [
|
|
11
|
+
{
|
|
12
|
+
name: 'Vercel',
|
|
13
|
+
imageUrl: 'https://assets.vercel.com/image/upload/front/favicon/vercel/180x180.png',
|
|
14
|
+
url: 'https://vercel.com',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'Next.js',
|
|
18
|
+
imageUrl: 'https://assets.vercel.com/image/upload/v1662130559/nextjs/Icon_dark_background.png',
|
|
19
|
+
url: 'https://nextjs.org',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'Supabase',
|
|
23
|
+
imageUrl: 'https://supabase.com/brand-assets/supabase-logo-icon.png',
|
|
24
|
+
url: 'https://supabase.com',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Stripe',
|
|
28
|
+
imageUrl: 'https://images.ctfassets.net/fzn2n1nzq965/HTTOloNPhisV9P4hlMPNA/cacf1bb88b9fc492dfad34378d844280/Stripe_icon_-_square.svg',
|
|
29
|
+
url: 'https://stripe.com',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { FieldDefinition } from '@nextsparkjs/core/types/blocks'
|
|
2
|
+
import {
|
|
3
|
+
baseContentFields,
|
|
4
|
+
baseDesignFields,
|
|
5
|
+
baseAdvancedFields,
|
|
6
|
+
} from '@nextsparkjs/core/types/blocks'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Logo Cloud Block Field Definitions
|
|
10
|
+
*
|
|
11
|
+
* Organized into 3 tabs:
|
|
12
|
+
* - Content: title, content, cta (from base) + logos array
|
|
13
|
+
* - Design: backgroundColor (from base) + layout, columns, grayscale, size
|
|
14
|
+
* - Advanced: className, id (from base)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Logo Cloud-specific content fields
|
|
18
|
+
const logoCloudContentFields: FieldDefinition[] = [
|
|
19
|
+
{
|
|
20
|
+
name: 'logos',
|
|
21
|
+
label: 'Logos',
|
|
22
|
+
type: 'array',
|
|
23
|
+
tab: 'content',
|
|
24
|
+
required: true,
|
|
25
|
+
description: 'List of client/partner logos to display',
|
|
26
|
+
helpText: 'Add up to 20 logos with image, alt text, and optional link',
|
|
27
|
+
minItems: 1,
|
|
28
|
+
maxItems: 20,
|
|
29
|
+
itemFields: [
|
|
30
|
+
{
|
|
31
|
+
name: 'image',
|
|
32
|
+
label: 'Logo Image',
|
|
33
|
+
type: 'image',
|
|
34
|
+
tab: 'content',
|
|
35
|
+
required: true,
|
|
36
|
+
helpText: 'Logo image URL (recommended: transparent PNG, 200x100px)',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'alt',
|
|
40
|
+
label: 'Alt Text',
|
|
41
|
+
type: 'text',
|
|
42
|
+
tab: 'content',
|
|
43
|
+
required: true,
|
|
44
|
+
placeholder: 'Company Name',
|
|
45
|
+
helpText: 'Descriptive alt text for accessibility',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'url',
|
|
49
|
+
label: 'Link URL (Optional)',
|
|
50
|
+
type: 'url',
|
|
51
|
+
tab: 'content',
|
|
52
|
+
required: false,
|
|
53
|
+
placeholder: 'https://example.com',
|
|
54
|
+
helpText: 'Optional link when logo is clicked',
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
// Logo Cloud-specific design fields
|
|
61
|
+
const logoCloudDesignFields: FieldDefinition[] = [
|
|
62
|
+
{
|
|
63
|
+
name: 'layout',
|
|
64
|
+
label: 'Layout',
|
|
65
|
+
type: 'select',
|
|
66
|
+
tab: 'design',
|
|
67
|
+
required: false,
|
|
68
|
+
default: 'grid',
|
|
69
|
+
description: 'Layout style for logos',
|
|
70
|
+
options: [
|
|
71
|
+
{ label: 'Grid', value: 'grid' },
|
|
72
|
+
{ label: 'Row (Wrap)', value: 'row' },
|
|
73
|
+
{ label: 'Row (Scroll)', value: 'row-scroll' },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'columns',
|
|
78
|
+
label: 'Grid Columns',
|
|
79
|
+
type: 'select',
|
|
80
|
+
tab: 'design',
|
|
81
|
+
required: false,
|
|
82
|
+
default: '5',
|
|
83
|
+
description: 'Number of columns in grid layout (desktop)',
|
|
84
|
+
options: [
|
|
85
|
+
{ label: '3 Columns', value: '3' },
|
|
86
|
+
{ label: '4 Columns', value: '4' },
|
|
87
|
+
{ label: '5 Columns', value: '5' },
|
|
88
|
+
{ label: '6 Columns', value: '6' },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'grayscale',
|
|
93
|
+
label: 'Grayscale Effect',
|
|
94
|
+
type: 'checkbox',
|
|
95
|
+
tab: 'design',
|
|
96
|
+
required: false,
|
|
97
|
+
default: true,
|
|
98
|
+
description: 'Show logos in grayscale (color on hover)',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'size',
|
|
102
|
+
label: 'Logo Size',
|
|
103
|
+
type: 'select',
|
|
104
|
+
tab: 'design',
|
|
105
|
+
required: false,
|
|
106
|
+
default: 'md',
|
|
107
|
+
description: 'Size of logos',
|
|
108
|
+
options: [
|
|
109
|
+
{ label: 'Small', value: 'sm' },
|
|
110
|
+
{ label: 'Medium', value: 'md' },
|
|
111
|
+
{ label: 'Large', value: 'lg' },
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Complete field definitions organized by tab
|
|
118
|
+
*/
|
|
119
|
+
export const fieldDefinitions: FieldDefinition[] = [
|
|
120
|
+
// Content tab: base fields + logo-cloud-specific
|
|
121
|
+
...baseContentFields,
|
|
122
|
+
...logoCloudContentFields,
|
|
123
|
+
|
|
124
|
+
// Design tab: base fields + logo-cloud-specific
|
|
125
|
+
...baseDesignFields,
|
|
126
|
+
...logoCloudDesignFields,
|
|
127
|
+
|
|
128
|
+
// Advanced tab: base fields only
|
|
129
|
+
...baseAdvancedFields,
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
// Alias for compatibility
|
|
133
|
+
export const fields = fieldDefinitions
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { baseBlockSchema } from '@nextsparkjs/core/types/blocks'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Logo Item Schema
|
|
6
|
+
* Individual logo in the cloud
|
|
7
|
+
*/
|
|
8
|
+
const logoItemSchema = z.object({
|
|
9
|
+
image: z.string().url('Must be a valid URL'),
|
|
10
|
+
alt: z.string().min(1, 'Alt text is required for accessibility'),
|
|
11
|
+
url: z.string().url('Must be a valid URL').optional(),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export type LogoItem = z.infer<typeof logoItemSchema>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Logo Cloud Block Schema
|
|
18
|
+
*
|
|
19
|
+
* Extends base schema with:
|
|
20
|
+
* - logos: Array of logo items (image, alt, optional url)
|
|
21
|
+
* - layout: Layout style (grid, row, row-scroll)
|
|
22
|
+
* - columns: Grid columns (3, 4, 5, 6)
|
|
23
|
+
* - grayscale: Show logos in grayscale with hover effect
|
|
24
|
+
* - size: Logo size (sm, md, lg)
|
|
25
|
+
*
|
|
26
|
+
* Note: Uses base schema title, content, cta, backgroundColor, className, id
|
|
27
|
+
*/
|
|
28
|
+
export const logoCloudSpecificSchema = z.object({
|
|
29
|
+
// Content: array of logo items
|
|
30
|
+
logos: z.array(logoItemSchema)
|
|
31
|
+
.min(1, 'At least one logo is required')
|
|
32
|
+
.max(20, 'Maximum 20 logos allowed'),
|
|
33
|
+
|
|
34
|
+
// Design: layout and styling
|
|
35
|
+
layout: z.enum(['grid', 'row', 'row-scroll']).default('grid'),
|
|
36
|
+
columns: z.enum(['3', '4', '5', '6']).default('5'),
|
|
37
|
+
grayscale: z.boolean().default(true),
|
|
38
|
+
size: z.enum(['sm', 'md', 'lg']).default('md'),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Complete Logo Cloud Block Schema
|
|
43
|
+
*/
|
|
44
|
+
export const schema = baseBlockSchema.merge(logoCloudSpecificSchema)
|
|
45
|
+
|
|
46
|
+
export type LogoCloudBlockProps = z.infer<typeof schema>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { cn } from '@nextsparkjs/core/lib/utils'
|
|
3
|
+
import { buildSectionClasses } from '@nextsparkjs/core/types/blocks'
|
|
4
|
+
import { sel } from '../../lib/selectors'
|
|
5
|
+
import type { PostContentBlockProps } from './schema'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Post Content Block Component
|
|
9
|
+
*
|
|
10
|
+
* Beautiful editorial typography optimized for long-form reading.
|
|
11
|
+
* Inspired by Medium, The New York Times, and other premium publishers.
|
|
12
|
+
*
|
|
13
|
+
* Props from 3-tab structure:
|
|
14
|
+
* - Content: title, content (rich-text article body), cta
|
|
15
|
+
* - Design: backgroundColor, drop cap, typography, dividers
|
|
16
|
+
* - Advanced: className, id
|
|
17
|
+
*
|
|
18
|
+
* Features:
|
|
19
|
+
* - Optimal reading width (680-900px)
|
|
20
|
+
* - Beautiful line height (1.7-1.8) for readability
|
|
21
|
+
* - Optional decorative drop cap
|
|
22
|
+
* - Configurable typography scale
|
|
23
|
+
* - Section dividers for visual breaks
|
|
24
|
+
*/
|
|
25
|
+
export function PostContentBlock({
|
|
26
|
+
// Base content props
|
|
27
|
+
title,
|
|
28
|
+
content,
|
|
29
|
+
cta,
|
|
30
|
+
// Base design props
|
|
31
|
+
backgroundColor,
|
|
32
|
+
// Post-content-specific design
|
|
33
|
+
showDropCap = false,
|
|
34
|
+
dropCapStyle = 'serif',
|
|
35
|
+
maxWidth = 'narrow',
|
|
36
|
+
fontSize = 'medium',
|
|
37
|
+
lineHeight = 'relaxed',
|
|
38
|
+
paragraphSpacing = 'normal',
|
|
39
|
+
showDividers = false,
|
|
40
|
+
dividerStyle = 'line',
|
|
41
|
+
// Base advanced props
|
|
42
|
+
className,
|
|
43
|
+
id,
|
|
44
|
+
}: PostContentBlockProps) {
|
|
45
|
+
// Max width classes for optimal reading
|
|
46
|
+
const maxWidthClasses: Record<string, string> = {
|
|
47
|
+
narrow: 'max-w-[680px]', // Optimal for reading
|
|
48
|
+
medium: 'max-w-[768px]',
|
|
49
|
+
wide: 'max-w-[900px]',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Font size classes
|
|
53
|
+
const fontSizeClasses: Record<string, string> = {
|
|
54
|
+
small: 'text-base', // 16px
|
|
55
|
+
medium: 'text-lg', // 18px
|
|
56
|
+
large: 'text-xl', // 20px
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Line height classes
|
|
60
|
+
const lineHeightClasses: Record<string, string> = {
|
|
61
|
+
compact: 'leading-relaxed', // 1.6
|
|
62
|
+
normal: 'leading-loose', // 1.7
|
|
63
|
+
relaxed: 'leading-[1.8]', // 1.8
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Paragraph spacing classes
|
|
67
|
+
const paragraphSpacingClasses: Record<string, string> = {
|
|
68
|
+
tight: '[&_p]:mb-4',
|
|
69
|
+
normal: '[&_p]:mb-6',
|
|
70
|
+
loose: '[&_p]:mb-8',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Drop cap style classes
|
|
74
|
+
const dropCapClasses: Record<string, string> = {
|
|
75
|
+
serif: 'font-serif',
|
|
76
|
+
'sans-serif': 'font-sans',
|
|
77
|
+
decorative: 'font-serif italic',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Build section classes with background
|
|
81
|
+
const sectionClasses = buildSectionClasses(
|
|
82
|
+
'py-16 px-4 md:py-24',
|
|
83
|
+
{ backgroundColor, className }
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Drop cap CSS (applied via first-letter pseudo-element)
|
|
87
|
+
const contentWithDropCap = showDropCap
|
|
88
|
+
? `[&>p:first-of-type]:first-letter:float-left [&>p:first-of-type]:first-letter:text-7xl [&>p:first-of-type]:first-letter:font-bold [&>p:first-of-type]:first-letter:mr-3 [&>p:first-of-type]:first-letter:mt-1 [&>p:first-of-type]:first-letter:leading-none [&>p:first-of-type]:first-letter:${dropCapClasses[dropCapStyle]}`
|
|
89
|
+
: ''
|
|
90
|
+
|
|
91
|
+
// Divider component
|
|
92
|
+
const Divider = () => {
|
|
93
|
+
if (!showDividers) return null
|
|
94
|
+
|
|
95
|
+
const dividerContent: Record<string, React.ReactNode> = {
|
|
96
|
+
line: <hr className="border-t border-gray-300 dark:border-gray-700" />,
|
|
97
|
+
dots: (
|
|
98
|
+
<div className="text-center text-gray-400 dark:text-gray-600 text-2xl tracking-[0.5em]">
|
|
99
|
+
•••
|
|
100
|
+
</div>
|
|
101
|
+
),
|
|
102
|
+
asterisks: (
|
|
103
|
+
<div className="text-center text-gray-400 dark:text-gray-600 text-xl tracking-widest">
|
|
104
|
+
* * *
|
|
105
|
+
</div>
|
|
106
|
+
),
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div className="my-12" data-cy={sel('blocks.postContent.divider')}>
|
|
111
|
+
{dividerContent[dividerStyle]}
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<section id={id} className={sectionClasses} data-cy={sel('blocks.postContent.container')}>
|
|
118
|
+
<div className="container mx-auto">
|
|
119
|
+
{/* Optional Section Title */}
|
|
120
|
+
{title && (
|
|
121
|
+
<div className={cn('mb-12 text-center', maxWidthClasses[maxWidth], 'mx-auto')}>
|
|
122
|
+
<h2 className="text-4xl font-bold md:text-5xl lg:text-6xl tracking-tight">
|
|
123
|
+
{title}
|
|
124
|
+
</h2>
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{/* Rich Text Article Content - Editorial Typography */}
|
|
129
|
+
<article
|
|
130
|
+
className={cn(
|
|
131
|
+
// Base typography
|
|
132
|
+
'prose prose-lg dark:prose-invert',
|
|
133
|
+
// Max width for optimal reading
|
|
134
|
+
maxWidthClasses[maxWidth],
|
|
135
|
+
'mx-auto',
|
|
136
|
+
// Font size
|
|
137
|
+
fontSizeClasses[fontSize],
|
|
138
|
+
// Line height
|
|
139
|
+
lineHeightClasses[lineHeight],
|
|
140
|
+
// Paragraph spacing
|
|
141
|
+
paragraphSpacingClasses[paragraphSpacing],
|
|
142
|
+
// Drop cap
|
|
143
|
+
contentWithDropCap,
|
|
144
|
+
// Enhanced prose styling for editorial content
|
|
145
|
+
'prose-headings:font-bold prose-headings:tracking-tight',
|
|
146
|
+
'prose-h2:text-3xl prose-h2:mt-12 prose-h2:mb-6',
|
|
147
|
+
'prose-h3:text-2xl prose-h3:mt-10 prose-h3:mb-4',
|
|
148
|
+
'prose-h4:text-xl prose-h4:mt-8 prose-h4:mb-3',
|
|
149
|
+
// Links
|
|
150
|
+
'prose-a:text-primary prose-a:no-underline prose-a:font-medium hover:prose-a:underline',
|
|
151
|
+
// Blockquotes (pull quotes)
|
|
152
|
+
'prose-blockquote:border-l-4 prose-blockquote:border-primary',
|
|
153
|
+
'prose-blockquote:pl-6 prose-blockquote:italic',
|
|
154
|
+
'prose-blockquote:text-xl prose-blockquote:leading-relaxed',
|
|
155
|
+
'prose-blockquote:my-8',
|
|
156
|
+
// Lists
|
|
157
|
+
'prose-ul:my-6 prose-ol:my-6',
|
|
158
|
+
'prose-li:my-2',
|
|
159
|
+
// Images
|
|
160
|
+
'prose-img:rounded-lg prose-img:shadow-lg',
|
|
161
|
+
'prose-img:my-8',
|
|
162
|
+
// Code
|
|
163
|
+
'prose-code:bg-gray-100 dark:prose-code:bg-gray-800',
|
|
164
|
+
'prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded',
|
|
165
|
+
'prose-code:text-sm',
|
|
166
|
+
// Pre/code blocks
|
|
167
|
+
'prose-pre:bg-gray-900 prose-pre:text-gray-100',
|
|
168
|
+
'prose-pre:rounded-lg prose-pre:my-6',
|
|
169
|
+
// Strong/bold
|
|
170
|
+
'prose-strong:font-semibold prose-strong:text-gray-900 dark:prose-strong:text-gray-100',
|
|
171
|
+
// Em/italic
|
|
172
|
+
'prose-em:italic'
|
|
173
|
+
)}
|
|
174
|
+
dangerouslySetInnerHTML={{ __html: content }}
|
|
175
|
+
/>
|
|
176
|
+
|
|
177
|
+
{/* Optional Divider after content */}
|
|
178
|
+
{showDividers && <Divider />}
|
|
179
|
+
|
|
180
|
+
{/* Optional CTA */}
|
|
181
|
+
{cta && (
|
|
182
|
+
<div className={cn('mt-12 text-center', maxWidthClasses[maxWidth], 'mx-auto')}>
|
|
183
|
+
<a
|
|
184
|
+
href={cta.link}
|
|
185
|
+
target={cta.target}
|
|
186
|
+
rel={cta.target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
187
|
+
className="inline-flex items-center justify-center rounded-md bg-primary px-8 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors shadow-md hover:shadow-lg"
|
|
188
|
+
data-cy={sel('blocks.postContent.cta')}
|
|
189
|
+
>
|
|
190
|
+
{cta.text}
|
|
191
|
+
</a>
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
</section>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BlockConfig } from '@nextsparkjs/core/types/blocks'
|
|
2
|
+
|
|
3
|
+
export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {
|
|
4
|
+
slug: 'post-content',
|
|
5
|
+
name: 'Post Content',
|
|
6
|
+
description: 'A rich text content block designed for long-form blog posts with editorial styling similar to Medium. Features beautiful typography, proper spacing, and optional elements like drop caps, pull quotes, and images.',
|
|
7
|
+
category: 'content',
|
|
8
|
+
icon: 'FileText',
|
|
9
|
+
thumbnail: '/theme/blocks/post-content/thumbnail.png',
|
|
10
|
+
scope: ['posts']
|
|
11
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BlockExample } from '@nextsparkjs/core/types/blocks'
|
|
2
|
+
|
|
3
|
+
export const examples: BlockExample[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Default',
|
|
6
|
+
description: 'Standard blog post content',
|
|
7
|
+
props: {
|
|
8
|
+
content: `
|
|
9
|
+
<h2>Introduction</h2>
|
|
10
|
+
<p>This is a sample blog post demonstrating the post content block. It supports rich text formatting including headings, paragraphs, lists, and more.</p>
|
|
11
|
+
|
|
12
|
+
<h3>Key Features</h3>
|
|
13
|
+
<ul>
|
|
14
|
+
<li>Rich text editing with markdown support</li>
|
|
15
|
+
<li>Automatic table of contents generation</li>
|
|
16
|
+
<li>Code syntax highlighting</li>
|
|
17
|
+
<li>Image optimization</li>
|
|
18
|
+
</ul>
|
|
19
|
+
|
|
20
|
+
<h3>Getting Started</h3>
|
|
21
|
+
<p>To use this block, simply add it to your page and start writing. The editor supports all standard formatting options and will automatically handle styling to match your theme.</p>
|
|
22
|
+
|
|
23
|
+
<blockquote>
|
|
24
|
+
<p>"This platform has transformed how we create and manage content. The simplicity is unmatched." - Happy Customer</p>
|
|
25
|
+
</blockquote>
|
|
26
|
+
|
|
27
|
+
<p>Learn more by exploring our <a href="/docs">documentation</a> or reaching out to our support team.</p>
|
|
28
|
+
`,
|
|
29
|
+
backgroundColor: 'white',
|
|
30
|
+
maxWidth: '4xl',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
]
|