blocks-dusted 0.1.1 → 0.1.2

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.
Files changed (50) hide show
  1. package/README.md +967 -3
  2. package/package.json +2 -2
  3. package/src/commands/add.js +143 -45
  4. package/src/commands/list.js +2 -2
  5. package/src/installers/checkRequirements.js +1 -0
  6. package/src/installers/copyTemplateFiles.js +23 -2
  7. package/src/installers/patchPayloadConfigCollections.js +92 -0
  8. package/src/installers/writeInstalledBlockReadme.js +17 -4
  9. package/src/registry/listTemplates.js +16 -7
  10. package/src/registry/loadTemplateManifest.js +20 -11
  11. package/src/registry/validateTemplateManifest.js +6 -5
  12. package/src/utils/paths.js +7 -0
  13. package/templates/blocks/DD-BookCall/Component.tsx +535 -573
  14. package/templates/blocks/DD-CardTemplate01/Component.tsx +45 -49
  15. package/templates/blocks/DD-Carousel-A/Component.tsx +249 -266
  16. package/templates/blocks/DD-Carousel-B/Component.tsx +403 -432
  17. package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -272
  18. package/templates/blocks/DD-Contact/Component.tsx +434 -439
  19. package/templates/blocks/DD-Contact/config.ts +8 -3
  20. package/templates/blocks/DD-FeatCat/Component.tsx +302 -361
  21. package/templates/blocks/DD-FeatCat/config.ts +201 -204
  22. package/templates/blocks/DD-FeatStrip/Component.tsx +171 -201
  23. package/templates/blocks/DD-Hero/Component.tsx +297 -317
  24. package/templates/blocks/DD-HorizontalScroll/Component.tsx +244 -303
  25. package/templates/blocks/DD-MasonaryMedia/Component.tsx +202 -228
  26. package/templates/blocks/DD-MasonaryMedia/config.ts +13 -14
  27. package/templates/blocks/DD-Pricing/Component.tsx +372 -380
  28. package/templates/blocks/DD-Process/Component.tsx +149 -155
  29. package/templates/blocks/DD-Section/Component.tsx +266 -327
  30. package/templates/blocks/DD-Services/Component.tsx +176 -188
  31. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -317
  32. package/templates/blocks/DD-Slider-A/Component.tsx +237 -252
  33. package/templates/blocks/DD-StackCards/Component.tsx +137 -160
  34. package/templates/blocks/DD-Team/Component.tsx +247 -265
  35. package/templates/blocks/DD-TechStack/Component.tsx +57 -72
  36. package/templates/blocks/DD-Testimonials/Component.tsx +147 -147
  37. package/templates/blocks/DD-Testimonials/config.ts +66 -66
  38. package/templates/blocks/DD-Work/Component.tsx +315 -328
  39. package/templates/blocks/DD-Work-B/Component.tsx +294 -320
  40. package/templates/collections/Testimonials/README.md +11 -0
  41. package/templates/collections/Testimonials/Testimonials.ts +161 -0
  42. package/templates/collections/Testimonials/manifest.json +55 -0
  43. package/templates/components/RichText/README.md +13 -0
  44. package/templates/components/RichText/converter/componentConverter/blocks.tsx +43 -0
  45. package/templates/components/RichText/converter/componentConverter/types.ts +11 -0
  46. package/templates/components/RichText/converter/index.tsx +18 -0
  47. package/templates/components/RichText/converter/internalLinks.tsx +45 -0
  48. package/templates/components/RichText/converter/textConverter.tsx +27 -0
  49. package/templates/components/RichText/index.tsx +35 -0
  50. package/templates/components/RichText/manifest.json +80 -0
@@ -0,0 +1,11 @@
1
+ # Testimonials Collection Template
2
+
3
+ Installs `src/collections/Testimonials.ts` and registers `Testimonials` in `payload.config.ts` when the collections array can be patched safely.
4
+
5
+ The source was adapted from the Raw Dusted Blocks Testimonials collection for the default Payload starter helper paths:
6
+
7
+ - `../access/authenticated`
8
+ - `../access/anyone`
9
+ - `@/fields/defaultLexical`
10
+
11
+ Review the installed collection before running Payload type generation.
@@ -0,0 +1,161 @@
1
+ import type { CollectionConfig } from 'payload'
2
+
3
+ import { authenticated } from '../access/authenticated'
4
+ import { anyone } from '../access/anyone'
5
+ import { defaultLexical } from '@/fields/defaultLexical'
6
+
7
+ export const Testimonials: CollectionConfig = {
8
+ slug: 'testimonials',
9
+ orderable: true,
10
+ labels: {
11
+ singular: 'Testimonial',
12
+ plural: 'Testimonials',
13
+ },
14
+ access: {
15
+ create: authenticated,
16
+ delete: authenticated,
17
+ read: anyone,
18
+ update: authenticated,
19
+ },
20
+ admin: {
21
+ useAsTitle: 'title',
22
+ defaultColumns: ['clientName', 'designation', 'company'],
23
+ },
24
+ fields: [
25
+ {
26
+ name: 'title',
27
+ type: 'text',
28
+ required: true,
29
+ index: true,
30
+ admin: {
31
+ description: 'Use client name as title to identify this testimonial.',
32
+ },
33
+ },
34
+ {
35
+ type: 'row',
36
+ fields: [
37
+ {
38
+ name: 'clientName',
39
+ type: 'richText',
40
+ label: "Client's Name",
41
+ required: true,
42
+ editor: defaultLexical,
43
+ admin: {
44
+ width: '50%',
45
+ },
46
+ },
47
+ {
48
+ name: 'designation',
49
+ hidden: false,
50
+ type: 'richText',
51
+ label: 'Designation / Role',
52
+ required: false,
53
+ editor: defaultLexical,
54
+ admin: {
55
+ width: '50%',
56
+ },
57
+ },
58
+ ],
59
+ },
60
+ {
61
+ type: 'row',
62
+ fields: [
63
+ {
64
+ name: 'company',
65
+ type: 'richText',
66
+ label: 'Company',
67
+ required: false,
68
+ editor: defaultLexical,
69
+ admin: {
70
+ width: '50%',
71
+ },
72
+ },
73
+ {
74
+ type: 'group',
75
+ label: false,
76
+ admin: {
77
+ width: '50%',
78
+ },
79
+ fields: [
80
+ {
81
+ name: 'companyLogo',
82
+ type: 'upload',
83
+ relationTo: 'media',
84
+ label: 'Company Logo / Avatar',
85
+ required: false,
86
+ },
87
+ {
88
+ name: 'fallbackAvatarUrl',
89
+ type: 'text',
90
+ label: 'Fallback Logo/Avatar URL',
91
+ admin: {
92
+ description: 'External URL for avatar when no media upload is provided.',
93
+ },
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ {
100
+ name: 'content',
101
+ type: 'richText',
102
+ label: 'Testimonial Content',
103
+ required: false,
104
+ editor: defaultLexical,
105
+ },
106
+ {
107
+ type: 'row',
108
+ fields: [
109
+ {
110
+ name: 'rating',
111
+ type: 'number',
112
+ label: 'Star Rating (1-5)',
113
+ min: 1,
114
+ max: 5,
115
+ admin: {
116
+ description: 'Numeric star rating used by Studio Testimonials block.',
117
+ width: '50%',
118
+ },
119
+ },
120
+ {
121
+ name: 'date',
122
+ type: 'text',
123
+ label: 'Date',
124
+ admin: {
125
+ description: 'Date string shown on marquee cards e.g. "March 2025".',
126
+ width: '50%',
127
+ },
128
+ },
129
+ {
130
+ name: 'ratingText',
131
+ type: 'text',
132
+ label: 'Rating (Text)',
133
+ admin: {
134
+ description: 'Displayed rating string e.g. "5 stars" or "5.0" used by Marquee block.',
135
+ width: '100%',
136
+ },
137
+ },
138
+ ],
139
+ },
140
+ {
141
+ name: 'sourceLogo',
142
+ type: 'upload',
143
+ relationTo: 'media',
144
+ label: 'Source Platform Logo',
145
+ admin: {
146
+ description: 'Platform logo for marquee cards, e.g. Upwork or Fiverr logo.',
147
+ },
148
+ },
149
+ {
150
+ name: 'sourceLogoAlt',
151
+ type: 'text',
152
+ label: 'Source Logo Alt Text',
153
+ defaultValue: 'logo alt',
154
+ admin: {
155
+ description: 'Alt text for the source platform logo.',
156
+ },
157
+ },
158
+ ],
159
+ }
160
+
161
+ export default Testimonials
@@ -0,0 +1,55 @@
1
+ {
2
+ "type": "collection",
3
+ "name": "Testimonials",
4
+ "label": "Testimonials Collection",
5
+ "slug": "testimonials",
6
+ "version": "0.0.1",
7
+ "source": {
8
+ "referencePath": "Raw Dusted Blocks/dusted-src (to add into CLI)/src/collections/Testimonials.ts"
9
+ },
10
+ "files": [
11
+ {
12
+ "from": "Testimonials.ts",
13
+ "to": "src/collections/Testimonials.ts"
14
+ }
15
+ ],
16
+ "dependencies": [],
17
+ "requiredProjectFiles": [
18
+ {
19
+ "path": "src/access/authenticated.ts",
20
+ "reason": "Testimonials uses the default authenticated access helper."
21
+ },
22
+ {
23
+ "path": "src/access/anyone.ts",
24
+ "reason": "Testimonials uses the default anyone read access helper."
25
+ },
26
+ {
27
+ "path": "src/fields/defaultLexical.ts",
28
+ "reason": "Testimonials richText fields use the project's defaultLexical editor helper."
29
+ },
30
+ {
31
+ "path": "src/collections/Media.ts",
32
+ "reason": "Testimonials upload fields relate to the media collection."
33
+ }
34
+ ],
35
+ "optionalProjectFiles": [
36
+ {
37
+ "path": "src/payload.config.ts",
38
+ "reason": "Installer can register Testimonials in the Payload collections array."
39
+ }
40
+ ],
41
+ "payloadConfigRegistration": {
42
+ "enabled": true,
43
+ "exportName": "Testimonials",
44
+ "importPath": "./collections/Testimonials"
45
+ },
46
+ "manual": [
47
+ "Review src/collections/Testimonials.ts against the target project's access helper paths, media slug, and defaultLexical helper before using it in production.",
48
+ "Run the target project's Payload type-generation command after installing the collection.",
49
+ "Run formatter, linter, TypeScript, and build checks after installation."
50
+ ],
51
+ "notes": [
52
+ "This collection is based on the Raw Dusted Blocks Testimonials reference, adjusted to the default Payload starter access helper paths.",
53
+ "The installer patches payload.config collections registration only when it can find a clear collections array."
54
+ ]
55
+ }
@@ -0,0 +1,13 @@
1
+ # RichText Component Template
2
+
3
+ This template restructures the default Payload starter RichText component into the Payload Rich Text Dusted Standard folder shape.
4
+
5
+ It is intentionally limited to default Payload starter behaviour:
6
+
7
+ - internal page and post links
8
+ - Lexical text formatting
9
+ - embedded `banner`, `code`, and `mediaBlock` blocks
10
+
11
+ Do not add custom reference-project block mappings unless the target project's RichText editor configuration enables those blocks.
12
+
13
+ The current `blocks-dusted` installer is still block-template-first, so this component template is source material for the next component installer milestone rather than an installable block template.
@@ -0,0 +1,43 @@
1
+ import type React from 'react'
2
+ import type { SerializedBlockNode } from '@payloadcms/richtext-lexical'
3
+
4
+ import { BannerBlock } from '@/blocks/Banner/Component'
5
+ import { CodeBlock } from '@/blocks/Code/Component'
6
+ import { MediaBlock } from '@/blocks/MediaBlock/Component'
7
+ import type { CodeBlockProps } from '@/blocks/Code/Component'
8
+ import type {
9
+ BannerBlock as BannerBlockProps,
10
+ MediaBlock as MediaBlockProps,
11
+ } from '@/payload-types'
12
+
13
+ type BlockConverter<T> = (props: { node?: SerializedBlockNode<T> }) => React.ReactNode
14
+
15
+ const banner: BlockConverter<BannerBlockProps> = ({ node }) => {
16
+ if (!node?.fields) return null
17
+ return <BannerBlock className="col-start-2 mb-4" {...node.fields} />
18
+ }
19
+
20
+ const mediaBlock: BlockConverter<MediaBlockProps> = ({ node }) => {
21
+ if (!node?.fields) return null
22
+ return (
23
+ <MediaBlock
24
+ className="col-start-1 col-span-3"
25
+ imgClassName="m-0"
26
+ {...node.fields}
27
+ captionClassName="mx-auto max-w-[48rem]"
28
+ enableGutter={false}
29
+ disableInnerContainer={true}
30
+ />
31
+ )
32
+ }
33
+
34
+ const code: BlockConverter<CodeBlockProps> = ({ node }) => {
35
+ if (!node?.fields) return null
36
+ return <CodeBlock className="col-start-2" {...node.fields} />
37
+ }
38
+
39
+ export const blockConverters = {
40
+ banner,
41
+ mediaBlock,
42
+ code,
43
+ }
@@ -0,0 +1,11 @@
1
+ import type { DefaultNodeTypes, SerializedBlockNode } from '@payloadcms/richtext-lexical'
2
+
3
+ import type { CodeBlockProps } from '@/blocks/Code/Component'
4
+ import type {
5
+ BannerBlock as BannerBlockProps,
6
+ MediaBlock as MediaBlockProps,
7
+ } from '@/payload-types'
8
+
9
+ export type RichTextEmbeddedBlock = BannerBlockProps | MediaBlockProps | CodeBlockProps
10
+
11
+ export type NodeTypes = DefaultNodeTypes | SerializedBlockNode<RichTextEmbeddedBlock>
@@ -0,0 +1,18 @@
1
+ import {
2
+ JSXConvertersFunction,
3
+ LinkJSXConverter,
4
+ } from '@payloadcms/richtext-lexical/react'
5
+
6
+ import { internalDocToHref } from '@/components/RichText/converter/internalLinks'
7
+ import { textConverter } from '@/components/RichText/converter/textConverter'
8
+ import { blockConverters } from '@/components/RichText/converter/componentConverter/blocks'
9
+ import type { NodeTypes } from '@/components/RichText/converter/componentConverter/types'
10
+
11
+ export type { NodeTypes } from '@/components/RichText/converter/componentConverter/types'
12
+
13
+ export const jsxConverters: JSXConvertersFunction<NodeTypes> = ({ defaultConverters }) => ({
14
+ ...defaultConverters,
15
+ ...LinkJSXConverter({ internalDocToHref }),
16
+ ...textConverter,
17
+ blocks: blockConverters,
18
+ })
@@ -0,0 +1,45 @@
1
+ import type { SerializedLinkNode } from '@payloadcms/richtext-lexical'
2
+
3
+ type LinkedDocument = {
4
+ slug?: string | null
5
+ breadcrumbs?: Array<{
6
+ slug?: string | null
7
+ url?: string | null
8
+ }> | null
9
+ }
10
+
11
+ const pagePathFromDoc = (value: LinkedDocument): string => {
12
+ const crumbs = Array.isArray(value.breadcrumbs) ? value.breadcrumbs : []
13
+ const lastCrumb = crumbs[crumbs.length - 1]
14
+
15
+ if (typeof lastCrumb?.url === 'string' && lastCrumb.url) return lastCrumb.url
16
+
17
+ const segments = crumbs
18
+ .map((crumb) => (typeof crumb?.slug === 'string' ? crumb.slug : ''))
19
+ .filter(Boolean)
20
+
21
+ if (segments.length > 0) return `/${segments.join('/')}`
22
+
23
+ const slug = typeof value.slug === 'string' ? value.slug : ''
24
+ if (!slug || slug === 'home') return '/'
25
+ return `/${slug.replace(/^\/+/, '')}`
26
+ }
27
+
28
+ export const internalDocToHref = ({ linkNode }: { linkNode: SerializedLinkNode }) => {
29
+ const doc = linkNode.fields.doc
30
+ const relationTo = doc?.relationTo
31
+ const value = doc?.value
32
+
33
+ if (!relationTo || typeof value !== 'object' || value === null) return '#'
34
+
35
+ switch (relationTo) {
36
+ case 'pages':
37
+ return pagePathFromDoc(value as LinkedDocument)
38
+ case 'posts': {
39
+ const slug = (value as LinkedDocument).slug
40
+ return typeof slug === 'string' && slug ? `/posts/${slug}` : '#'
41
+ }
42
+ default:
43
+ return '#'
44
+ }
45
+ }
@@ -0,0 +1,27 @@
1
+ import type React from 'react'
2
+ import type { SerializedTextNode } from '@payloadcms/richtext-lexical'
3
+ import type { JSXConverters } from '@payloadcms/richtext-lexical/react'
4
+
5
+ const IS_BOLD = 1
6
+ const IS_ITALIC = 2
7
+ const IS_STRIKETHROUGH = 4
8
+ const IS_UNDERLINE = 8
9
+ const IS_CODE = 16
10
+ const IS_SUBSCRIPT = 32
11
+ const IS_SUPERSCRIPT = 64
12
+
13
+ export const textConverter: JSXConverters<SerializedTextNode> = {
14
+ text: ({ node }) => {
15
+ let output: React.ReactNode = node.text
16
+
17
+ if (node.format & IS_BOLD) output = <strong>{output}</strong>
18
+ if (node.format & IS_ITALIC) output = <em>{output}</em>
19
+ if (node.format & IS_STRIKETHROUGH) output = <s>{output}</s>
20
+ if (node.format & IS_UNDERLINE) output = <u>{output}</u>
21
+ if (node.format & IS_CODE) output = <code>{output}</code>
22
+ if (node.format & IS_SUBSCRIPT) output = <sub>{output}</sub>
23
+ if (node.format & IS_SUPERSCRIPT) output = <sup>{output}</sup>
24
+
25
+ return output
26
+ },
27
+ }
@@ -0,0 +1,35 @@
1
+ import type React from 'react'
2
+ import { type DefaultTypedEditorState } from '@payloadcms/richtext-lexical'
3
+ import { RichText as ConvertRichText } from '@payloadcms/richtext-lexical/react'
4
+
5
+ import { jsxConverters } from '@/components/RichText/converter'
6
+ import { cn } from '@/utilities/ui'
7
+
8
+ type Props = {
9
+ data?: DefaultTypedEditorState | null
10
+ enableGutter?: boolean
11
+ enableProse?: boolean
12
+ } & React.HTMLAttributes<HTMLDivElement>
13
+
14
+ export default function RichText(props: Props) {
15
+ const { className, enableProse = true, enableGutter = true, data, ...rest } = props
16
+
17
+ if (!data) return null
18
+
19
+ return (
20
+ <ConvertRichText
21
+ converters={jsxConverters}
22
+ data={data}
23
+ className={cn(
24
+ 'payload-richtext',
25
+ {
26
+ container: enableGutter,
27
+ 'max-w-none': !enableGutter,
28
+ 'mx-auto prose md:prose-md dark:prose-invert': enableProse,
29
+ },
30
+ className,
31
+ )}
32
+ {...rest}
33
+ />
34
+ )
35
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "type": "component",
3
+ "name": "RichText",
4
+ "label": "Payload RichText Dusted Standard",
5
+ "slug": "RichText",
6
+ "version": "0.0.1",
7
+ "source": {
8
+ "defaultPath": "src/components/RichText/index.tsx",
9
+ "referencePath": "Raw Dusted Blocks/dusted-src (to add into CLI)/src/components/RichText"
10
+ },
11
+ "files": [
12
+ {
13
+ "from": "index.tsx",
14
+ "to": "src/components/RichText/index.tsx"
15
+ },
16
+ {
17
+ "from": "converter/index.tsx",
18
+ "to": "src/components/RichText/converter/index.tsx"
19
+ },
20
+ {
21
+ "from": "converter/internalLinks.tsx",
22
+ "to": "src/components/RichText/converter/internalLinks.tsx"
23
+ },
24
+ {
25
+ "from": "converter/textConverter.tsx",
26
+ "to": "src/components/RichText/converter/textConverter.tsx"
27
+ },
28
+ {
29
+ "from": "converter/componentConverter/blocks.tsx",
30
+ "to": "src/components/RichText/converter/componentConverter/blocks.tsx"
31
+ },
32
+ {
33
+ "from": "converter/componentConverter/types.ts",
34
+ "to": "src/components/RichText/converter/componentConverter/types.ts"
35
+ }
36
+ ],
37
+ "dependencies": [
38
+ {
39
+ "name": "@payloadcms/richtext-lexical"
40
+ }
41
+ ],
42
+ "requiredProjectFiles": [
43
+ {
44
+ "path": "src/utilities/ui.ts",
45
+ "reason": "RichText uses cn from @/utilities/ui."
46
+ },
47
+ {
48
+ "path": "src/blocks/Banner/Component.tsx",
49
+ "reason": "Default Payload RichText embedded block mapping for banner."
50
+ },
51
+ {
52
+ "path": "src/blocks/Code/Component.tsx",
53
+ "reason": "Default Payload RichText embedded block mapping for code."
54
+ },
55
+ {
56
+ "path": "src/blocks/MediaBlock/Component.tsx",
57
+ "reason": "Default Payload RichText embedded block mapping for mediaBlock."
58
+ }
59
+ ],
60
+ "optionalProjectFiles": [
61
+ {
62
+ "path": "src/collections/Posts/index.ts",
63
+ "reason": "Inspect RichText editor features and embedded BlocksFeature configuration before installing."
64
+ }
65
+ ],
66
+ "manual": [
67
+ "The existing src/components/RichText folder is backed up before installation; review the backup and copy any project-specific behaviour missing from the newly installed RichText structure.",
68
+ "Install this component only after inspecting the target project\u0027s RichText editor configuration.",
69
+ "This default template maps only the Payload starter embedded RichText blocks: banner, code, and mediaBlock.",
70
+ "Add cta or other block mappings only if the target RichText field\u0027s BlocksFeature actually enables those blocks.",
71
+ "Keep internal link routes aligned with the target project\u0027s real pages/posts routes.",
72
+ "Run the target project\u0027s Payload type-generation command if generated types are stale.",
73
+ "Run formatter, linter, TypeScript, and build checks after installation."
74
+ ],
75
+ "notes": [
76
+ "This is a Layer 2 reusable core component template, not a Payload block template.",
77
+ "Do not copy reference-project preview-link-card, custom media, custom color state, or non-starter block mappings into default Payload installs."
78
+ ],
79
+ "backupExistingDestination": true
80
+ }