@hubspot/cms-component-library 0.1.0-alpha.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/README.md +3 -0
- package/cli/commands/customize.ts +145 -0
- package/cli/commands/help.ts +56 -0
- package/cli/commands/version.ts +12 -0
- package/cli/index.ts +42 -0
- package/cli/tests/commands.test.ts +128 -0
- package/cli/tests/get-file.test.ts +82 -0
- package/cli/tests/version-integration.test.ts +39 -0
- package/cli/utils/cli-metadata.ts +9 -0
- package/cli/utils/component-naming.ts +76 -0
- package/cli/utils/components.ts +74 -0
- package/cli/utils/file-operations.ts +158 -0
- package/cli/utils/logging.ts +13 -0
- package/cli/utils/prompts.ts +80 -0
- package/cli/utils/version.ts +33 -0
- package/components/componentLibrary/Button/index.module.scss +9 -0
- package/components/componentLibrary/Button/index.tsx +83 -0
- package/components/componentLibrary/Button/scaffolds/fields.tsx.template +70 -0
- package/components/componentLibrary/Button/scaffolds/index.ts.template +95 -0
- package/components/componentLibrary/Heading/index.module.scss +9 -0
- package/components/componentLibrary/Heading/index.tsx +34 -0
- package/components/componentLibrary/Heading/scaffolds/fields.tsx.template +62 -0
- package/components/componentLibrary/Heading/scaffolds/index.ts.template +46 -0
- package/components/componentLibrary/index.ts +1 -0
- package/components/componentLibrary/styles/_component-base.scss +246 -0
- package/components/componentLibrary/types/index.ts +308 -0
- package/components/componentLibrary/utils/chainApi/choiceFieldGenerator.tsx +64 -0
- package/components/componentLibrary/utils/chainApi/index.ts +115 -0
- package/components/componentLibrary/utils/chainApi/labelGenerator.ts +76 -0
- package/components/componentLibrary/utils/chainApi/stateManager.ts +178 -0
- package/components/componentLibrary/utils/classname.ts +40 -0
- package/components/componentLibrary/utils/createConditionalClasses.ts +44 -0
- package/components/componentLibrary/utils/createHsclComponent.tsx +167 -0
- package/components/componentLibrary/utils/propResolution/createCssVariables.ts +58 -0
- package/components/componentLibrary/utils/propResolution/propResolutionUtils.ts +113 -0
- package/components/componentLibrary/utils/storybook/standardArgs.ts +607 -0
- package/package.json +62 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { promises as fs, readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
export const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
export const getFileContents = (filePath: string): string => {
|
|
9
|
+
const fullPath = path.join(__dirname, filePath);
|
|
10
|
+
try {
|
|
11
|
+
return readFileSync(fullPath, 'utf8');
|
|
12
|
+
} catch (error: unknown) {
|
|
13
|
+
const errorMessage = `Error reading file at path "${fullPath}": ${
|
|
14
|
+
error instanceof Error ? error.message : 'Unknown error occurred'
|
|
15
|
+
}`;
|
|
16
|
+
|
|
17
|
+
throw new Error(errorMessage);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get the components directory in the current working directory
|
|
23
|
+
* Creates it if it doesn't exist
|
|
24
|
+
*/
|
|
25
|
+
async function getComponentsDirectory(): Promise<string> {
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
const componentsPath = path.join(cwd, 'components');
|
|
28
|
+
|
|
29
|
+
// Create components directory if it doesn't exist
|
|
30
|
+
await fs.mkdir(componentsPath, { recursive: true });
|
|
31
|
+
|
|
32
|
+
return componentsPath;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface CopyScaffoldsOptions {
|
|
36
|
+
sourceComponent: string;
|
|
37
|
+
targetName: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Copy scaffold files from a source component, replacing template variables
|
|
42
|
+
* @param sourceComponent - The name of the component to copy scaffolds from
|
|
43
|
+
* @param targetName - The new name (prefix + component name) to replace __NC__
|
|
44
|
+
* @param outputDir - Optional output directory, defaults to current working directory
|
|
45
|
+
*/
|
|
46
|
+
export async function copyScaffolds({
|
|
47
|
+
sourceComponent,
|
|
48
|
+
targetName,
|
|
49
|
+
}: CopyScaffoldsOptions): Promise<void> {
|
|
50
|
+
const sourceDir = path.join(
|
|
51
|
+
__dirname,
|
|
52
|
+
'../../components/componentLibrary',
|
|
53
|
+
sourceComponent,
|
|
54
|
+
'scaffolds'
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Check if scaffolds directory exists
|
|
58
|
+
try {
|
|
59
|
+
await fs.access(sourceDir);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Scaffolds directory not found for component: ${sourceComponent}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get components directory and create hscl subdirectory
|
|
67
|
+
const componentsDir = await getComponentsDirectory();
|
|
68
|
+
const hsclDirectory = path.join(componentsDir, 'hscl');
|
|
69
|
+
const targetDir = path.join(hsclDirectory, targetName);
|
|
70
|
+
|
|
71
|
+
// Check if target directory already exists
|
|
72
|
+
try {
|
|
73
|
+
await fs.access(targetDir);
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Component "${targetName}" already exists in /components/hscl/. Please choose a different prefix or remove the existing component.`
|
|
76
|
+
);
|
|
77
|
+
} catch (error: unknown) {
|
|
78
|
+
// If access fails with ENOENT, the directory doesn't exist (which is what we want)
|
|
79
|
+
// For any other error, re-throw it
|
|
80
|
+
if (
|
|
81
|
+
error instanceof Error &&
|
|
82
|
+
error.message &&
|
|
83
|
+
!error.message.includes('ENOENT')
|
|
84
|
+
) {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
// For ENOENT errors, continue (directory doesn't exist, which is good)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
91
|
+
|
|
92
|
+
// Read all files in scaffolds directory
|
|
93
|
+
const files = await fs.readdir(sourceDir);
|
|
94
|
+
const templateFiles = files.filter(file => file.endsWith('.template'));
|
|
95
|
+
|
|
96
|
+
await Promise.all(
|
|
97
|
+
templateFiles.map(async templateFile => {
|
|
98
|
+
const sourceFilePath = path.join(sourceDir, templateFile);
|
|
99
|
+
const targetFileName = templateFile.replace('.template', '');
|
|
100
|
+
const targetFilePath = path.join(targetDir, targetFileName);
|
|
101
|
+
|
|
102
|
+
// Read template content using the get-file utility
|
|
103
|
+
const relativePath = path.relative(__dirname, sourceFilePath);
|
|
104
|
+
const templateContent = getFileContents(relativePath);
|
|
105
|
+
|
|
106
|
+
// Replace template variables
|
|
107
|
+
const processedContent = templateContent.replace(/__NC__/g, targetName);
|
|
108
|
+
|
|
109
|
+
// Write to target file
|
|
110
|
+
await fs.writeFile(targetFilePath, processedContent, 'utf8');
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Copy scaffolds for multiple components
|
|
117
|
+
*/
|
|
118
|
+
export async function copyMultipleScaffolds({
|
|
119
|
+
sourceComponents,
|
|
120
|
+
prefix,
|
|
121
|
+
}: {
|
|
122
|
+
sourceComponents: string[];
|
|
123
|
+
prefix: string;
|
|
124
|
+
}): Promise<{
|
|
125
|
+
successful: string[];
|
|
126
|
+
skipped: string[];
|
|
127
|
+
errors: Array<{ component: string; error: string }>;
|
|
128
|
+
}> {
|
|
129
|
+
const results = {
|
|
130
|
+
successful: [],
|
|
131
|
+
skipped: [],
|
|
132
|
+
errors: [],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
for (const component of sourceComponents) {
|
|
136
|
+
const targetName = `${prefix}${component}`;
|
|
137
|
+
try {
|
|
138
|
+
await copyScaffolds({
|
|
139
|
+
sourceComponent: component,
|
|
140
|
+
targetName,
|
|
141
|
+
});
|
|
142
|
+
results.successful.push(component);
|
|
143
|
+
} catch (error: unknown) {
|
|
144
|
+
const errorMessage =
|
|
145
|
+
error instanceof Error ? error.message : String(error);
|
|
146
|
+
|
|
147
|
+
// Check if this is a "component already exists" error
|
|
148
|
+
if (errorMessage.includes('already exists')) {
|
|
149
|
+
results.skipped.push(component);
|
|
150
|
+
} else {
|
|
151
|
+
results.errors.push({ component, error: errorMessage });
|
|
152
|
+
console.error(`❌ Error creating ${targetName}: ${errorMessage}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export const logSuccess = (message: string): void => {
|
|
4
|
+
console.log(chalk.green(message));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const logError = (message: string): void => {
|
|
8
|
+
console.error(chalk.red(message));
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const logWarning = (message: string): void => {
|
|
12
|
+
console.error(chalk.magenta(message));
|
|
13
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
// Helper for styling help text
|
|
5
|
+
const helpText = (text: string): string => chalk.dim.gray(text);
|
|
6
|
+
|
|
7
|
+
interface SelectPromptOptions {
|
|
8
|
+
type: 'select' | 'multiselect';
|
|
9
|
+
message: string;
|
|
10
|
+
choices: (string | { title: string; value: string })[];
|
|
11
|
+
helpMessage?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface TextPromptOptions {
|
|
15
|
+
message: string;
|
|
16
|
+
defaultValue: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Reusable prompt functions
|
|
20
|
+
export const selectPrompt = async ({
|
|
21
|
+
type,
|
|
22
|
+
message,
|
|
23
|
+
choices,
|
|
24
|
+
helpMessage,
|
|
25
|
+
}: SelectPromptOptions) => {
|
|
26
|
+
const fullMessage = helpMessage
|
|
27
|
+
? `${message} ${helpText(helpMessage)}`
|
|
28
|
+
: message;
|
|
29
|
+
|
|
30
|
+
return await prompts({
|
|
31
|
+
type: type,
|
|
32
|
+
name: 'value',
|
|
33
|
+
message: fullMessage,
|
|
34
|
+
choices: choices.map(choice =>
|
|
35
|
+
typeof choice === 'string' ? { title: choice, value: choice } : choice
|
|
36
|
+
),
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const multiselectPrompt = async ({
|
|
41
|
+
message,
|
|
42
|
+
choices,
|
|
43
|
+
helpMessage,
|
|
44
|
+
}: Omit<SelectPromptOptions, 'type'>): Promise<{ value: string[] }> => {
|
|
45
|
+
return await selectPrompt({
|
|
46
|
+
type: 'multiselect',
|
|
47
|
+
message,
|
|
48
|
+
helpMessage,
|
|
49
|
+
choices: choices.map(choice =>
|
|
50
|
+
typeof choice === 'string' ? { title: choice, value: choice } : choice
|
|
51
|
+
),
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const textPromptWithDefault = async ({
|
|
56
|
+
message,
|
|
57
|
+
defaultValue,
|
|
58
|
+
}: TextPromptOptions): Promise<string> => {
|
|
59
|
+
const { value } = await prompts({
|
|
60
|
+
type: 'text',
|
|
61
|
+
name: 'value',
|
|
62
|
+
message: `${message} ${helpText(`(default: ${defaultValue})`)}`,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return value || defaultValue;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const promptForNamingPrefix = async (
|
|
69
|
+
defaultValue: string,
|
|
70
|
+
numberOfComponents: number
|
|
71
|
+
): Promise<string> => {
|
|
72
|
+
const isMultipleComponents = numberOfComponents > 1;
|
|
73
|
+
|
|
74
|
+
return await textPromptWithDefault({
|
|
75
|
+
message: `What prefix should we use for your new component${
|
|
76
|
+
isMultipleComponents ? 's' : ''
|
|
77
|
+
}?`,
|
|
78
|
+
defaultValue,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getFileContents } from './file-operations.js';
|
|
2
|
+
|
|
3
|
+
interface PackageJson {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
license?: string;
|
|
8
|
+
scripts?: Record<string, string>;
|
|
9
|
+
repository?: {
|
|
10
|
+
type: string;
|
|
11
|
+
url: string;
|
|
12
|
+
};
|
|
13
|
+
dependencies?: Record<string, string>;
|
|
14
|
+
devDependencies?: Record<string, string>;
|
|
15
|
+
author?: string;
|
|
16
|
+
'lint-staged'?: Record<string, string[]>;
|
|
17
|
+
resolutions?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getVersion = (): string => {
|
|
21
|
+
try {
|
|
22
|
+
const packageJson: PackageJson = JSON.parse(
|
|
23
|
+
getFileContents('../../package.json')
|
|
24
|
+
);
|
|
25
|
+
return packageJson.version;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(
|
|
28
|
+
'Error reading package.json:',
|
|
29
|
+
error instanceof Error ? error.message : error
|
|
30
|
+
);
|
|
31
|
+
return '1.0.0';
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
import createHsclComponent from '../utils/createHsclComponent.js';
|
|
4
|
+
import styles from './index.module.scss';
|
|
5
|
+
import { BaseAndInternalComponentProps } from '../types/index.js';
|
|
6
|
+
|
|
7
|
+
// Base props shared by all variants
|
|
8
|
+
type BaseButtonProps = BaseAndInternalComponentProps & {
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Button variant props
|
|
14
|
+
type ButtonVariantProps = {
|
|
15
|
+
buttonType: 'button';
|
|
16
|
+
onClick?: () => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Link variant props
|
|
20
|
+
type LinkVariantProps = {
|
|
21
|
+
buttonType: 'link';
|
|
22
|
+
href?: string;
|
|
23
|
+
target?: string;
|
|
24
|
+
rel?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Main component props using discriminated union
|
|
28
|
+
export type ButtonProps = BaseButtonProps &
|
|
29
|
+
(ButtonVariantProps | LinkVariantProps);
|
|
30
|
+
|
|
31
|
+
const componentName = 'button';
|
|
32
|
+
|
|
33
|
+
const Component = (props: ButtonProps) => {
|
|
34
|
+
// Extract shared/base props that are always consumed
|
|
35
|
+
const {
|
|
36
|
+
buttonType = 'link',
|
|
37
|
+
hsclInternal: { hsclProcessedClasses, hsclProcessedStyles },
|
|
38
|
+
...rest
|
|
39
|
+
} = props;
|
|
40
|
+
|
|
41
|
+
// Shared props for both elements
|
|
42
|
+
const sharedProps = {
|
|
43
|
+
className: hsclProcessedClasses,
|
|
44
|
+
style: hsclProcessedStyles,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function renderButton(
|
|
48
|
+
props: Omit<ButtonVariantProps, 'buttonType' | 'hsclInternal'> &
|
|
49
|
+
Partial<BaseButtonProps>
|
|
50
|
+
) {
|
|
51
|
+
const { onClick, disabled = false, children, ...rest } = props;
|
|
52
|
+
return (
|
|
53
|
+
<button {...sharedProps} onClick={onClick} disabled={disabled} {...rest}>
|
|
54
|
+
{children}
|
|
55
|
+
</button>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function renderLink(
|
|
60
|
+
props: Omit<LinkVariantProps, 'buttonType' | 'hsclInternal'> &
|
|
61
|
+
Partial<BaseButtonProps>
|
|
62
|
+
) {
|
|
63
|
+
const { href = '', target = '_self', rel = '', children, ...rest } = props;
|
|
64
|
+
return (
|
|
65
|
+
<a {...sharedProps} href={href} target={target} rel={rel} {...rest}>
|
|
66
|
+
{children}
|
|
67
|
+
</a>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// TypeScript properly discriminates based on buttonType
|
|
72
|
+
if (buttonType === 'button') return renderButton(rest);
|
|
73
|
+
|
|
74
|
+
// For link variant
|
|
75
|
+
return renderLink(rest);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
Component.hsclComponentName = componentName;
|
|
79
|
+
Component.cssModule = styles;
|
|
80
|
+
|
|
81
|
+
const Button = createHsclComponent(Component);
|
|
82
|
+
|
|
83
|
+
export default Button;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { LinkField, TextField } from '@hubspot/cms-components/fields'
|
|
2
|
+
import __NC__ from './buttonIndex.js';
|
|
3
|
+
|
|
4
|
+
export type __NC__ContentFieldsProps = {
|
|
5
|
+
buttonTextDefault?: string;
|
|
6
|
+
buttonTextLabel?: string;
|
|
7
|
+
buttonTextName?: string;
|
|
8
|
+
buttonLinkLabel?: string;
|
|
9
|
+
buttonLinkName?: string;
|
|
10
|
+
buttonLinkDefault?: {url: {type: 'EXTERNAL', content_id: number, href: string}};
|
|
11
|
+
showButtonLink?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function contentFields(props: __NC__ContentFieldsProps) {
|
|
15
|
+
const { buttonTextDefault, buttonTextLabel, buttonTextName, buttonLinkLabel, buttonLinkName, buttonLinkDefault, showButtonLink = true } = props;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<TextField
|
|
20
|
+
label={buttonTextLabel || 'Button Text'}
|
|
21
|
+
name={buttonTextName || 'buttonText'}
|
|
22
|
+
default={buttonTextDefault || 'Button Text'}
|
|
23
|
+
/>
|
|
24
|
+
{showButtonLink && (
|
|
25
|
+
<LinkField
|
|
26
|
+
label={buttonLinkLabel || 'Button Link'}
|
|
27
|
+
name={buttonLinkName || 'buttonLink'}
|
|
28
|
+
default={buttonLinkDefault || {url: {type: 'EXTERNAL', content_id: 0, href: 'https://www.google.com'}}}
|
|
29
|
+
/>
|
|
30
|
+
)}
|
|
31
|
+
</>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type __NC__StyleFieldsProps = {
|
|
36
|
+
buttonVariantLabel?: string;
|
|
37
|
+
buttonVariantName?: string;
|
|
38
|
+
buttonVariantDefault?: string;
|
|
39
|
+
buttonSizeLabel?: string;
|
|
40
|
+
buttonSizeName?: string;
|
|
41
|
+
buttonSizeDefault?: string;
|
|
42
|
+
buttonBorderRadiusLabel?: string;
|
|
43
|
+
buttonBorderRadiusName?: string;
|
|
44
|
+
buttonBorderRadiusDefault?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function styleFields(props: __NC__StyleFieldsProps) {
|
|
48
|
+
const { buttonVariantLabel, buttonVariantName, buttonVariantDefault, buttonSizeLabel, buttonSizeName, buttonSizeDefault, buttonBorderRadiusLabel, buttonBorderRadiusName, buttonBorderRadiusDefault } = props;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<>
|
|
52
|
+
<__NC__.DimensionChoiceFields.variant
|
|
53
|
+
label={buttonVariantLabel || 'Variant'}
|
|
54
|
+
name={buttonVariantName || 'buttonVariant'}
|
|
55
|
+
default={buttonVariantDefault || 'solid'}
|
|
56
|
+
/>
|
|
57
|
+
<__NC__.DimensionChoiceFields.size
|
|
58
|
+
label={buttonSizeLabel || 'Size'}
|
|
59
|
+
name={buttonSizeName || 'buttonSize'}
|
|
60
|
+
default={buttonSizeDefault || 'medium'}
|
|
61
|
+
/>
|
|
62
|
+
<__NC__.DimensionChoiceFields.borderRadius
|
|
63
|
+
label={buttonBorderRadiusLabel || 'Border Radius'}
|
|
64
|
+
name={buttonBorderRadiusName || 'buttonBorderRadius'}
|
|
65
|
+
default={buttonBorderRadiusDefault || 'medium'}
|
|
66
|
+
/>
|
|
67
|
+
</>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Button, { ButtonProps } from '@hubspot/cms-component-library/Button';
|
|
2
|
+
import { createComponentInstance } from '@hubspot/cms-component-library';
|
|
3
|
+
import { __NC__ContentFieldsProps, __NC__StyleFieldsProps, contentFields, styleFields } from './fields.js';
|
|
4
|
+
|
|
5
|
+
export type __NC__Props = ButtonProps & {
|
|
6
|
+
variant: 'solid' | 'outline' | 'unstyled';
|
|
7
|
+
size: 'small' | 'medium' | 'large' | 'xlarge';
|
|
8
|
+
borderRadius: 'none' | 'small' | 'medium' | 'large' | 'full';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const __NC__ = createComponentInstance<__NC__Props>(Button)
|
|
12
|
+
.withContentFields<__NC__ContentFieldsProps>(contentFields)
|
|
13
|
+
.withStyleFields<__NC__StyleFieldsProps>(styleFields);
|
|
14
|
+
|
|
15
|
+
const sharedVariantProps = {
|
|
16
|
+
display: 'inline-block',
|
|
17
|
+
textDecoration: 'none',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
__NC__.setDimension('variant')
|
|
21
|
+
.setOption('solid')
|
|
22
|
+
.setProps({
|
|
23
|
+
backgroundColor: 'black',
|
|
24
|
+
color: 'white',
|
|
25
|
+
hoverBackgroundColor: '#262626',
|
|
26
|
+
...sharedVariantProps
|
|
27
|
+
})
|
|
28
|
+
.setOption('outline')
|
|
29
|
+
.setProps({
|
|
30
|
+
backgroundColor: 'transparent',
|
|
31
|
+
color: 'black',
|
|
32
|
+
border: '1px solid black',
|
|
33
|
+
hoverBackgroundColor: '#E6E6E6',
|
|
34
|
+
...sharedVariantProps
|
|
35
|
+
})
|
|
36
|
+
.setOption('unstyled')
|
|
37
|
+
.setProps({})
|
|
38
|
+
.createDimensionChoiceField();
|
|
39
|
+
|
|
40
|
+
const sharedSizeProps = {
|
|
41
|
+
fontWeight: '600',
|
|
42
|
+
lineHeight: '1.3',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
__NC__.setDimension('size')
|
|
46
|
+
.setOption('small')
|
|
47
|
+
.setProps({
|
|
48
|
+
fontSize: '12px',
|
|
49
|
+
padding: '12px 16px',
|
|
50
|
+
...sharedSizeProps
|
|
51
|
+
})
|
|
52
|
+
.setOption('medium')
|
|
53
|
+
.setProps({
|
|
54
|
+
fontSize: '16px',
|
|
55
|
+
padding: '16px 20px',
|
|
56
|
+
...sharedSizeProps
|
|
57
|
+
})
|
|
58
|
+
.setOption('large')
|
|
59
|
+
.setProps({
|
|
60
|
+
fontSize: '24px',
|
|
61
|
+
padding: '20px 24px',
|
|
62
|
+
...sharedSizeProps
|
|
63
|
+
})
|
|
64
|
+
.setOption('extraLarge')
|
|
65
|
+
.setProps({
|
|
66
|
+
fontSize: '30px',
|
|
67
|
+
padding: '24px 30px',
|
|
68
|
+
...sharedSizeProps
|
|
69
|
+
})
|
|
70
|
+
.createDimensionChoiceField();
|
|
71
|
+
|
|
72
|
+
__NC__.setDimension('borderRadius')
|
|
73
|
+
.setOption('none')
|
|
74
|
+
.setProps({
|
|
75
|
+
borderRadius: '0',
|
|
76
|
+
})
|
|
77
|
+
.setOption('small')
|
|
78
|
+
.setProps({
|
|
79
|
+
borderRadius: '4px',
|
|
80
|
+
})
|
|
81
|
+
.setOption('medium')
|
|
82
|
+
.setProps({
|
|
83
|
+
borderRadius: '8px',
|
|
84
|
+
})
|
|
85
|
+
.setOption('large')
|
|
86
|
+
.setProps({
|
|
87
|
+
borderRadius: '12px',
|
|
88
|
+
})
|
|
89
|
+
.setOption('full')
|
|
90
|
+
.setProps({
|
|
91
|
+
borderRadius: '50px',
|
|
92
|
+
})
|
|
93
|
+
.createDimensionChoiceField();
|
|
94
|
+
|
|
95
|
+
export default __NC__;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
import createHsclComponent from '../utils/createHsclComponent.js';
|
|
4
|
+
import styles from './index.module.scss';
|
|
5
|
+
import { BaseAndInternalComponentProps } from '../types/index.js';
|
|
6
|
+
|
|
7
|
+
export type HeadingProps = BaseAndInternalComponentProps & {
|
|
8
|
+
text: string;
|
|
9
|
+
headingLevel: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const componentName = 'heading';
|
|
13
|
+
|
|
14
|
+
const Component = (props: HeadingProps) => {
|
|
15
|
+
const {
|
|
16
|
+
headingLevel = 'h1',
|
|
17
|
+
hsclInternal: { hsclProcessedClasses, hsclProcessedStyles },
|
|
18
|
+
} = props;
|
|
19
|
+
|
|
20
|
+
const HeadingLevel = headingLevel;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<HeadingLevel style={hsclProcessedStyles} className={hsclProcessedClasses}>
|
|
24
|
+
{props.text}
|
|
25
|
+
</HeadingLevel>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
Component.hsclComponentName = componentName;
|
|
30
|
+
Component.cssModule = styles;
|
|
31
|
+
|
|
32
|
+
const Heading = createHsclComponent(Component);
|
|
33
|
+
|
|
34
|
+
export default Heading;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { __NC__ } from './index.js'
|
|
3
|
+
import { TextField, AlignmentField } from '@hubspot/cms-components/fields'
|
|
4
|
+
|
|
5
|
+
export type __NC__ContentFieldsProps = {
|
|
6
|
+
HeadingDefault?: string;
|
|
7
|
+
HeadingLabel?: string;
|
|
8
|
+
HeadingName?: string;
|
|
9
|
+
HeadingLevelLabel?: string;
|
|
10
|
+
HeadingLevelName?: string;
|
|
11
|
+
HeadingLevelDefault
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function contentFields(props: __NC__ContentFieldsProps) {
|
|
15
|
+
const { HeadingDefault, HeadingLabel, HeadingName, HeadingLevelLabel, HeadingLevelName, HeadingLevelDefault } = props;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<TextField
|
|
20
|
+
label={HeadingLabel || 'Heading Text'}
|
|
21
|
+
name={HeadingName || 'headingText'}
|
|
22
|
+
default={HeadingDefault || 'Default Heading Text'}
|
|
23
|
+
/>
|
|
24
|
+
<ChoiceField
|
|
25
|
+
label={HeadingLevelLabel || "Heading Level"}
|
|
26
|
+
name={HeadingLevelName || "headingLevel"}
|
|
27
|
+
choices={[['h1', 'h1'],['h2', 'h2'],['h3', 'h3'],['h4', 'h4'],['h5', 'h5'],['h6', 'h6']]}
|
|
28
|
+
default={HeadingLevelDefault || 'h1'}
|
|
29
|
+
/>
|
|
30
|
+
</>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type __NC__StyleFieldsProps = {
|
|
35
|
+
displayAsLabel?: string;
|
|
36
|
+
displayAsName?: string;
|
|
37
|
+
displayAsDefault?: string;
|
|
38
|
+
alignmentLabel?: string;
|
|
39
|
+
alignmentName?: string;
|
|
40
|
+
alignmentDefault?: 'LEFT' | 'CENTER' | 'RIGHT';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function styleFields(props: __NC__StyleFieldsProps) {
|
|
44
|
+
const { displayAsLabel, displayAsName, displayAsDefault, alignmentLabel, alignmentName, alignmentDefault } = props;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<AlignmentField
|
|
49
|
+
label={alignmentLabel || 'Heading Alignment'}
|
|
50
|
+
default={{ horizontal_align: alignmentDefault || 'LEFT' }}
|
|
51
|
+
name={alignmentName || 'headingAlignment'}
|
|
52
|
+
alignmentDirection="HORIZONTAL"
|
|
53
|
+
/>
|
|
54
|
+
<__NC__.DimensionChoiceFields.displayAs
|
|
55
|
+
label={displayAsLabel || 'Display as'}
|
|
56
|
+
default={displayAsDefault || 'h1'}
|
|
57
|
+
name={displayAsName || 'headingDisplayAs'}
|
|
58
|
+
/>
|
|
59
|
+
</>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createComponentInstance } from '@hubspot/cms-component-library/';
|
|
2
|
+
import Heading from '@hubspot/cms-component-library/Heading'
|
|
3
|
+
import { HeadingProps } from '@hubspot/cms-component-library/Heading'
|
|
4
|
+
import { __NC__ContentFieldsProps, __NC__StyleFieldsProps, contentFields, styleFields } from './fields.js';
|
|
5
|
+
|
|
6
|
+
export type __NC__Props = HeadingProps & {
|
|
7
|
+
displayAs: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const __NC__ = createComponentInstance<__NC__Props>(Heading)
|
|
11
|
+
.withContentFields<__NC__ContentFieldsProps>(contentFields)
|
|
12
|
+
.withStyleFields<__NC__StyleFieldsProps>(styleFields);
|
|
13
|
+
|
|
14
|
+
__NC__.setDimension('displayAs')
|
|
15
|
+
.setOption('h1')
|
|
16
|
+
.setProps({
|
|
17
|
+
fontSize: '2.125rem',
|
|
18
|
+
fontWeight: '700',
|
|
19
|
+
})
|
|
20
|
+
.setOption('h2')
|
|
21
|
+
.setProps({
|
|
22
|
+
fontSize: '1.875rem',
|
|
23
|
+
fontWeight: '700',
|
|
24
|
+
})
|
|
25
|
+
.setOption('h3')
|
|
26
|
+
.setProps({
|
|
27
|
+
fontSize: '1.5rem',
|
|
28
|
+
fontWeight: '700',
|
|
29
|
+
})
|
|
30
|
+
.setOption('h4')
|
|
31
|
+
.setProps({
|
|
32
|
+
fontSize: '1.25rem',
|
|
33
|
+
fontWeight: '700',
|
|
34
|
+
})
|
|
35
|
+
.setOption('h5')
|
|
36
|
+
.setProps({
|
|
37
|
+
fontSize: '1rem',
|
|
38
|
+
fontWeight: '700',
|
|
39
|
+
})
|
|
40
|
+
.setOption('h6')
|
|
41
|
+
.setProps({
|
|
42
|
+
fontSize: '0.875rem',
|
|
43
|
+
fontWeight: '700',
|
|
44
|
+
})
|
|
45
|
+
.createDimensionChoiceField();
|
|
46
|
+
|