Skip to content

Commit fa8daee

Browse files
committed
feat: add preset components and refactor to use them
1 parent 34cafb4 commit fa8daee

File tree

94 files changed

+1234
-1252
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1234
-1252
lines changed

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"formatter": {
1313
"enabled": true,
14-
"lineWidth": 100,
14+
"lineWidth": 120,
1515
"indentStyle": "space",
1616
"indentWidth": 2
1717
},
Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,7 @@
1-
.ActiveBlockControl {
1+
.container {
22
display: flex;
33
align-items: center;
44
justify-content: space-between;
5-
padding: 0.625rem 0.625rem 0.625rem 0.75rem;
6-
border-bottom: 1px solid var(--composify-palette-outline);
7-
}
8-
9-
.ActiveBlockControl-Title {
10-
margin: 0;
11-
font-size: 1rem;
12-
font-weight: 600;
13-
color: var(--composify-palette-on-surface);
14-
}
15-
16-
.ActiveBlockControl-Actions {
17-
display: flex;
18-
align-items: center;
19-
}
20-
21-
.ActiveBlockControl-ActionItem {
22-
display: flex;
23-
align-items: center;
24-
justify-content: center;
25-
cursor: pointer;
26-
padding: 0.5rem 0.5rem;
27-
border: none;
28-
background-color: transparent;
29-
fill: var(--composify-color-on-surface);
30-
}
31-
32-
.ActiveBlockControl-ActionItem:hover,
33-
.ActiveBlockControl-ActionItem:active {
34-
opacity: 0.7;
5+
padding: 10px 10px 10px 12px;
6+
border-top: 1px solid var(--composify-palette-outline);
357
}

packages/react/src/editor/ActiveBlockControl/ActiveBlockControl.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { getClassNameFactory } from '../../utils';
1+
import { CopyIcon, TrashIcon } from 'lucide-react';
2+
import { IconButton, Text } from '../../preset';
23
import { useEditing } from '../EditingContext';
34
import styles from './ActiveBlockControl.module.css';
45

5-
const getClassName = getClassNameFactory('ActiveBlockControl', styles);
6-
76
export const ActiveBlockControl = () => {
87
const { activeBlock, duplicateActiveBlock, removeActiveBlock } = useEditing();
98

@@ -12,19 +11,17 @@ export const ActiveBlockControl = () => {
1211
}
1312

1413
return (
15-
<div className={getClassName()}>
16-
<h2 className={getClassName('Title')}>{activeBlock?.type}</h2>
17-
<div className={getClassName('Actions')}>
18-
<button type="button" className={getClassName('ActionItem')} onClick={duplicateActiveBlock}>
19-
<svg xmlns="http://www.w3.org/2000/svg" width={16} height={16} viewBox="0 0 640 640">
20-
<path d="M288 64C252.7 64 224 92.7 224 128L224 384C224 419.3 252.7 448 288 448L480 448C515.3 448 544 419.3 544 384L544 183.4C544 166 536.9 149.3 524.3 137.2L466.6 81.8C454.7 70.4 438.8 64 422.3 64L288 64zM160 192C124.7 192 96 220.7 96 256L96 512C96 547.3 124.7 576 160 576L352 576C387.3 576 416 547.3 416 512L416 496L352 496L352 512L160 512L160 256L176 256L176 192L160 192z" />
21-
</svg>
22-
</button>
23-
<button type="button" className={getClassName('ActionItem')} onClick={removeActiveBlock}>
24-
<svg xmlns="http://www.w3.org/2000/svg" width={16} height={16} viewBox="0 0 640 640">
25-
<path d="M232.7 69.9L224 96L128 96C110.3 96 96 110.3 96 128C96 145.7 110.3 160 128 160L512 160C529.7 160 544 145.7 544 128C544 110.3 529.7 96 512 96L416 96L407.3 69.9C402.9 56.8 390.7 48 376.9 48L263.1 48C249.3 48 237.1 56.8 232.7 69.9zM512 208L128 208L149.1 531.1C150.7 556.4 171.7 576 197 576L443 576C468.3 576 489.3 556.4 490.9 531.1L512 208z" />
26-
</svg>
27-
</button>
14+
<div className={styles.container}>
15+
<Text size="md" weight="semibold" color="on-surface">
16+
{activeBlock.type}
17+
</Text>
18+
<div>
19+
<IconButton size="sm" onClick={duplicateActiveBlock}>
20+
<CopyIcon />
21+
</IconButton>
22+
<IconButton size="sm" onClick={removeActiveBlock}>
23+
<TrashIcon />
24+
</IconButton>
2825
</div>
2926
</div>
3027
);
Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,18 @@
1-
.BlockGroup {
1+
.container {
22
display: flex;
33
flex-direction: column;
44
}
55

6-
.BlockGroup-Category {
7-
margin: 0;
8-
padding: 0.5rem 0.75rem;
9-
font-size: 0.75rem;
10-
font-weight: 500;
6+
.category {
7+
padding: 8px 12px;
118
border-top: 1px solid var(--composify-palette-outline);
129
border-bottom: 1px solid var(--composify-palette-outline);
1310
background-color: var(--composify-palette-surface-container);
14-
color: var(--composify-palette-on-surface);
1511
}
1612

17-
.BlockGroup-BlockList {
13+
.grid {
1814
display: grid;
1915
grid-template-columns: repeat(2, 1fr);
20-
gap: 0.25rem;
21-
padding: 0.5rem;
22-
}
23-
24-
.BlockGroup-BlockItem {
25-
display: flex;
26-
flex-direction: column;
27-
align-items: center;
28-
gap: 0.375rem;
29-
border-radius: 0.25rem;
30-
padding: 0.25rem;
31-
overflow: hidden;
32-
}
33-
34-
.BlockGroup-BlockItemPreview {
35-
display: flex;
36-
justify-content: center;
37-
align-items: center;
38-
border-radius: 0.25rem;
39-
border: 1px solid var(--composify-palette-outline);
40-
background-color: var(--composify-palette-surface-container-low);
41-
overflow: hidden;
42-
width: 7.75rem;
43-
height: 7.75rem;
44-
}
45-
46-
.BlockGroup-BlockItemPreview:hover {
47-
border: 1px solid var(--composify-palette-primary);
48-
}
49-
50-
.BlockGroup-BlockName {
51-
align-self: stretch;
52-
margin: 0 0.1875rem;
53-
font-size: 0.75rem;
54-
color: var(--composify-palette-on-surface);
55-
overflow: hidden;
56-
text-overflow: ellipsis;
16+
gap: 4px;
17+
padding: 8px;
5718
}
Lines changed: 12 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,23 @@
1-
import type { FC, ReactNode } from 'react';
2-
import toJsxString from 'react-element-to-jsx-string';
3-
import { type Block, type Node, Parser, Renderer } from '../../renderer';
4-
import { getClassNameFactory } from '../../utils';
5-
import { TargetType } from '../Constants';
6-
import { ContentScaler } from '../ContentScaler';
7-
import { Draggable } from '../Draggable';
1+
import type { FC } from 'react';
2+
import { Text } from '../../preset';
3+
import type { Block } from '../../renderer';
4+
import { BlockItem } from '../BlockItem';
85
import styles from './BlockGroup.module.css';
96

10-
const getClassName = getClassNameFactory('BlockGroup', styles);
11-
127
type Props = {
138
category: string;
149
blocks: Block[];
1510
};
1611

1712
export const BlockGroup: FC<Props> = ({ category, blocks }) => (
18-
<div className={getClassName()}>
19-
<h4 className={getClassName('Category')}>{category}</h4>
20-
<div className={getClassName('BlockList')}>
21-
{blocks.map((block) => {
22-
const propertySpecs = block.props ?? {};
23-
24-
const defaultProps = Object.entries(propertySpecs).reduce(
25-
(acc, [key, value]) => {
26-
if (value?.hasDefault) {
27-
acc[key] =
28-
propertySpecs[key].type === 'node'
29-
? Parser.parse(toJsxString(value.default))
30-
: value.default;
31-
}
32-
33-
return acc;
34-
},
35-
{} as Record<string, unknown>,
36-
);
37-
38-
const children = [defaultProps.children]
39-
.flat()
40-
.filter((child) => typeof child !== 'undefined') as ReactNode;
41-
42-
const node = {
43-
__composify__: true,
44-
type: block.name,
45-
props: defaultProps,
46-
children,
47-
} as Node;
48-
49-
const jsxProps = Object.entries(defaultProps)
50-
.map(([key, value]) => `${key}={${JSON.stringify(value)}}`)
51-
.join(' ');
52-
53-
return (
54-
<div key={block.name} className={getClassName('BlockItem')}>
55-
<Draggable
56-
type={TargetType.Library}
57-
item={{
58-
...node,
59-
props: defaultProps,
60-
}}
61-
>
62-
<div className={getClassName('BlockItemPreview')}>
63-
<ContentScaler width={1024} height={1024}>
64-
<Renderer source={`<${block.name} ${jsxProps} />`} />
65-
</ContentScaler>
66-
</div>
67-
</Draggable>
68-
<p className={getClassName('BlockName')}>{block.name}</p>
69-
</div>
70-
);
71-
})}
13+
<div className={styles.container}>
14+
<Text size="xs" weight="medium" color="on-surface" className={styles.category}>
15+
{category}
16+
</Text>
17+
<div className={styles.grid}>
18+
{blocks.map((block) => (
19+
<BlockItem key={block.name} block={block} />
20+
))}
7221
</div>
7322
</div>
7423
);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.container {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 6px;
5+
border-radius: 4px;
6+
padding: 4px;
7+
overflow: hidden;
8+
}
9+
10+
.preview {
11+
display: flex;
12+
align-items: center;
13+
justify-content: center;
14+
width: 124px;
15+
height: 124px;
16+
overflow: hidden;
17+
border-radius: 4px;
18+
border: 1px solid var(--composify-palette-outline);
19+
background-color: var(--composify-palette-surface-container-low);
20+
transition: border 0.15s;
21+
}
22+
23+
.preview:hover {
24+
border: 1px solid var(--composify-palette-primary);
25+
}
26+
27+
.name {
28+
align-self: stretch;
29+
margin: 0 3px;
30+
overflow: hidden;
31+
text-overflow: ellipsis;
32+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { ReactNode } from 'react';
2+
import toJsxString from 'react-element-to-jsx-string';
3+
import { Text } from '../../preset';
4+
import { type Block, type Node, Parser, Renderer } from '../../renderer';
5+
import { TargetType } from '../Constants';
6+
import { ContentScaler } from '../ContentScaler';
7+
import { Draggable } from '../Draggable';
8+
import styles from './BlockItem.module.css';
9+
10+
type Props = {
11+
block: Block;
12+
};
13+
14+
export const BlockItem = ({ block }: Props) => {
15+
const propertySpecs = block.props ?? {};
16+
17+
const defaultProps = Object.entries(propertySpecs).reduce(
18+
(acc, [key, value]) => {
19+
if (value?.hasDefault) {
20+
acc[key] = propertySpecs[key].type === 'node' ? Parser.parse(toJsxString(value.default)) : value.default;
21+
}
22+
23+
return acc;
24+
},
25+
{} as Record<string, unknown>,
26+
);
27+
28+
const children = [defaultProps.children].flat().filter((child) => typeof child !== 'undefined') as ReactNode;
29+
30+
const node = {
31+
__composify__: true,
32+
type: block.name,
33+
props: defaultProps,
34+
children,
35+
} as Node;
36+
37+
const jsxProps = Object.entries(defaultProps)
38+
.map(([key, value]) => `${key}={${JSON.stringify(value)}}`)
39+
.join(' ');
40+
41+
return (
42+
<div className={styles.container}>
43+
<Draggable type={TargetType.Library} item={node}>
44+
<div className={styles.preview}>
45+
<ContentScaler width={1024} height={1024}>
46+
<Renderer source={`<${block.name} ${jsxProps} />`} />
47+
</ContentScaler>
48+
</div>
49+
</Draggable>
50+
<Text size="xs" color="on-surface" className={styles.name}>
51+
{block.name}
52+
</Text>
53+
</div>
54+
);
55+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { BlockItem } from './BlockItem';

0 commit comments

Comments
 (0)