Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support withComponent on all react-css components #245

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v21.6.2
v22.11.0
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
nodejs 21.6.2
nodejs 22.11.0
bun 1.1.33
37,746 changes: 18,525 additions & 19,221 deletions package-lock.json

Large diffs are not rendered by default.

186 changes: 128 additions & 58 deletions system/core/scripts/generateComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}

interface PropsType {
key: string;
value: string;
type: 'value' | 'string';
}

abstract class ComponentBuilder {
static headerComment = `/**
* DO NOT EDIT: This file is generated in the post-build step of @tablecheck/tablekit-core
* If you need to provide more "structure" to this component move it to the 'structuredComponents' folder
*/`;
* DO NOT EDIT: This file is generated in the post-build step of @tablecheck/tablekit-core
* If you need to provide more "structure" to this component move it to the 'structuredComponents' folder
*/`;

protected importKey: string;

Expand All @@ -29,7 +35,7 @@ abstract class ComponentBuilder {

protected structuredFileNames: string[];

protected fixedProps: string[];
protected fixedProps: PropsType[];

protected defaultProps: [string, string][];

Expand Down Expand Up @@ -82,6 +88,7 @@ abstract class ComponentBuilder {
this.fixedProps = [];
this.importKey = importedKey;
this.element = importedValues.element || 'div';
this.variantStyles = importedValues.variantStyles;
const defaultProps = importedValues.defaultProps
? { ...importedValues.defaultProps }
: {};
Expand Down Expand Up @@ -130,10 +137,13 @@ abstract class ComponentBuilder {
}

hasVariants() {
return (
typeof this.variantStyles === 'object' &&
Object.keys(this.variantStyles).length
);
return !!this.getVariants().length;
}

getVariants() {
return typeof this.variantStyles === 'object'
? Object.keys(this.variantStyles)
: [];
}

// eslint-disable-next-line class-methods-use-this
Expand All @@ -150,70 +160,91 @@ abstract class ComponentBuilder {
} & ${this.reactHtmlAttributesType};`;
}

writeForwardRefComponent({
abstract writeForwardRefComponent({
varName,
elementName,
omitVariants,
displayName,
fixedProps = this.fixedProps || []
}) {
return `export const ${varName} = ${this.writeForwardRefComponentFunction({
elementName,
omitVariants,
fixedProps
})};
${varName}.displayName = \`${displayName || varName}\`;`;
}
}): string;

writeForwardRefComponentFunction({
elementName,
omitVariants,
fixedProps: fixedPropsArg = this.fixedProps || []
}) {
const fixedProps = [...fixedPropsArg];
const props = this.buildProps(fixedPropsArg)
.map(
({ key, value, type }) =>
`${key}=${type === 'string' ? `"${value}"` : `{${value}}`}`
)
.join(' ');
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
return `React.forwardRef<${this.elementType}, ${propsType} & ${this.reactHtmlAttributesType}>((props, ref) => <${elementName} {...props} ${props} ref={ref} />)`;
}

protected buildProps(fixedProps: PropsType[]): PropsType[] {
const props = [...fixedProps];
this.defaultProps.forEach(([key]) => {
fixedProps.push(
`${key}={props${this.formatKey(key)} ?? (${
this.importKey
}.defaultProps${this.formatKey(key)} as never)}`
);
props.push({
key,
type: 'value',
value: this.formatDefaultProp(key)
});
});
if (
this.element === 'button' &&
!this.defaultProps.find(([key]) => key === 'type')
) {
fixedProps.push(`type="button" `);
props.push({ key: 'type', type: 'string', value: 'button' });
}
this.configurableProps.forEach(([propKey, configKey]) => {
fixedProps.push(
`${propKey}={props${this.formatKey(
propKey
)} ?? getConfigDefault('${configKey}')}`
);
props.push({
key: propKey,
type: 'value',
value: this.formatConfigurableProp(propKey, configKey)
});
});
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
return `React.forwardRef<${this.elementType}, ${propsType} & ${
this.reactHtmlAttributesType
}>((props, ref) => <${elementName} {...props} ${fixedProps.join(
' '
)} ref={ref} />)`;
return props;
}

protected formatDefaultProp(key: string) {
return `props${this.formatKey(key)} ?? (${
this.importKey
}.defaultProps${this.formatKey(key)} as never)`;
}

protected formatConfigurableProp(key, configKey) {
return `props${this.formatKey(key)} ?? getConfigDefault('${configKey}')`;
}
}

class CssComponentBuilder extends ComponentBuilder {
private className: string;

private selectors: string[];
get localImports() {
return [
...super.localImports,
this.hasVariants()
? `import { buildVariantComponents, buildWithComponent } from '../utils';`
: `import { buildWithComponent } from '../utils';`
];
}

constructor(importedKey, importedValues) {
super(cssOutputFolderPath, importedKey, importedValues);
this.className =
this.pascalImportKey === 'InputLikeButton'
? 'input'
: importedValues.className || '';
this.selectors = importedValues.selectors;
this.fixedProps = this.className
? [`className={\`\${(props.className ?? '')} ${this.className}\`}`]
? [
{
key: 'className',
type: 'string',
value: this.className
}
]
: [];
}

Expand Down Expand Up @@ -247,35 +278,75 @@ class CssComponentBuilder extends ComponentBuilder {
this.writeForwardRefComponent({
varName: this.pascalImportKey,
elementName: this.element,
omitVariants: false,
displayName: this.pascalImportKey
displayName: this.pascalImportKey,
shouldExport: true
})
);

if (this.hasVariants()) {
fileContent.push(
`export const Variant${this.pascalImportKey} = Object.entries(${
this.importKey
}.variantStyles).reduce(
(result, [key, value]) => {
result[\`\${key.charAt(0).toUpperCase()}\${key.slice(1).toLowerCase()}\`] = ${this.writeForwardRefComponentFunction(
{
elementName: this.element,
omitVariants: true,
fixedProps: [...this.fixedProps, `data-variant={key}`]
}
)};
return result;
},
{} as Record<Capitalize<${
this.pascalImportKey
}Variant>, React.ComponentType<${this.getBasePropsType()}>>
);`
`export const Variant${this.pascalImportKey} = buildVariantComponents<
${this.getVariants()
.map((v) => `'${v}'`)
.join(' | ')},
Props,
'${this.element}'
>({
variants: ${JSON.stringify(this.getVariants())},
className: '${this.className}',
element: '${this.element}',
displayName: '${this.pascalImportKey}'
});`
);
}

return fileContent;
}

writeForwardRefComponent({
varName,
elementName,
omitVariants = false,
displayName = '',
fixedProps: fixedPropsArg = this.fixedProps || [],
shouldExport = false
}) {
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
const props = this.buildProps(fixedPropsArg);
const classNameProp = props.find(({ key }) => key === 'className');
const append: string[] = [
`className: ${classNameProp ? `'${classNameProp.value}'` : 'undefined'}`
];
const additionalProps = props.filter(({ key }) => key !== 'className');
if (additionalProps.length) {
append.push(
`additionalProps: { ${additionalProps
.map(
({ key, value, type }) =>
`'${key}': ${type === 'string' ? `'${value}'` : value}`
)
.join(', ')} }`
);
}
return `${
shouldExport ? 'export ' : ''
}const ${varName} = buildWithComponent<${
this.elementType
}, ${propsType} & ${
this.reactHtmlAttributesType
}>({ tag: '${elementName}', displayName: '${
displayName || varName
}', ${append.join(', ')}});`;
}

protected formatDefaultProp(key: string) {
return `(${this.importKey}.defaultProps${this.formatKey(key)} as never)`;
}

// eslint-disable-next-line class-methods-use-this
protected formatConfigurableProp(key, configKey) {
return `{ toString: () => getConfigDefault('${configKey}') }`;
}
}

class ReactComponentBuilder extends ComponentBuilder {
Expand Down Expand Up @@ -304,7 +375,6 @@ class ReactComponentBuilder extends ComponentBuilder {

constructor(importedKey, importedValues) {
super(reactOutputFolderPath, importedKey, importedValues);
this.variantStyles = importedValues.variantStyles;
this.fullStyles = importedValues.fullStyles;
this.coreStyles = importedValues.coreStyles;
}
Expand Down
1 change: 0 additions & 1 deletion system/core/src/components/InputAlert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const fullStyles = css`
display: grid;
grid-template-columns: min-content 1fr;
gap: var(--spacing-l2);
margin-top: var(--spacing-l2);
color: var(--text);
border-radius: var(--border-radius-small);

Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/InputStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export const fullStyles = css`
.label-row {
display: flex;
justify-content: space-between;
align-items: baseline;
}
`;
6 changes: 3 additions & 3 deletions system/css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
"@tablecheck/tablekit-core": "^4.0.0",
"@types/fs-extra": "11.0.1",
"chalk": "5.2.0",
"cssnano": "5.1.15",
"cssnano": "7.0.6",
"execa": "6.1.0",
"fs-extra": "11.1.0",
"lodash": "4.17.21",
"pluralize": "8.0.0",
"postcss": "8.4.38",
"postcss-nesting": "12.1.5",
"postcss": "8.4.49",
"postcss-nesting": "13.0.1",
"typescript": "4.9.5"
},
"publishConfig": {
Expand Down
1 change: 1 addition & 0 deletions system/react-css/scripts/generateIndex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Promise.all(filenames.map(getExport)).then((exportLines) => {
* The exports here are generated from all ts/tsx files at the root level
*/
export * from './config';
export type { WithComponentType } from './utils';
${fileContent}`,
{ filepath, ...config }
)
Expand Down
26 changes: 0 additions & 26 deletions system/react-css/src/buildVariantComponents.ts

This file was deleted.

9 changes: 4 additions & 5 deletions system/react-css/src/components/Anchor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
import { anchor } from '@tablecheck/tablekit-core';
import * as React from 'react';

import { buildWithComponent } from '../utils';

export type Props = anchor.Props &
React.AnchorHTMLAttributes<HTMLAnchorElement>;

export const Anchor = React.forwardRef<
export const Anchor = buildWithComponent<
HTMLAnchorElement,
Props & React.AnchorHTMLAttributes<HTMLAnchorElement>
>((props, ref) => (
<a {...props} className={`${props.className ?? ''} link`} ref={ref} />
));
Anchor.displayName = `Anchor`;
>({ tag: 'a', displayName: 'Anchor', className: 'link' });
Loading
Loading