Skip to content

Commit a3e8586

Browse files
committed
feat: implement property grouping
1 parent 0426471 commit a3e8586

File tree

11 files changed

+109
-72
lines changed

11 files changed

+109
-72
lines changed

packages/react/src/editor/PropertyControl/PropertyControl.module.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
border: 1px solid var(--composify-palette-outline);
5757
color: var(--composify-palette-on-surface-variant);
5858
background-color: var(--composify-palette-surface-container-low);
59+
cursor: pointer;
5960
}
6061

6162
.removeButton {

packages/react/src/editor/PropertyControlGroup/PropertyControlGroup.module.css

Whitespace-only changes.

packages/react/src/editor/PropertyControlGroup/PropertyControlGroup.tsx

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/react/src/editor/PropertyControlGroup/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/react/src/editor/PropertyLibrary/PropertyLibrary.module.css renamed to packages/react/src/editor/PropertyGroup/PropertyGroup.module.css

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1-
.PropertyLibrary {
1+
.propertyGroup {
2+
padding: 12px;
3+
border-bottom: 1px solid var(--composify-palette-outline);
4+
}
5+
6+
.groupLabel {
7+
font-weight: 600;
8+
font-size: 12px;
9+
margin-bottom: 10px;
10+
}
11+
12+
.contentGrid {
213
display: grid;
314
grid-template-columns: 1fr 1fr;
415
grid-gap: 10px;
5-
padding: 12px;
6-
border-bottom: 1px solid var(--composify-palette-outline);
716
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/** biome-ignore-all lint/suspicious/noExplicitAny: for arbitrary props */
2+
import type { FC } from 'react';
3+
import { VStack } from '../../preset';
4+
import type { PropertySpec } from '../../renderer';
5+
import { createVariants } from '../../utils';
6+
import { PropertyControlArray } from '../PropertyControlArray';
7+
import { PropertyControlBoolean } from '../PropertyControlBoolean';
8+
import { PropertyControlCustom } from '../PropertyControlCustom';
9+
import { PropertyControlNode } from '../PropertyControlNode';
10+
import { PropertyControlNumber } from '../PropertyControlNumber';
11+
import { PropertyControlObject } from '../PropertyControlObject';
12+
import { PropertyControlRadio } from '../PropertyControlRadio';
13+
import { PropertyControlSelect } from '../PropertyControlSelect';
14+
import { PropertyControlText } from '../PropertyControlText';
15+
import { PropertyControlTextArea } from '../PropertyControlTextArea';
16+
import styles from './PropertyGroup.module.css';
17+
18+
const variants = createVariants(styles);
19+
20+
type Props = {
21+
group: string;
22+
items: Record<string, PropertySpec<any>>;
23+
};
24+
25+
export const PropertyGroup: FC<Props> = ({ group, items }) => (
26+
<VStack className={variants('propertyGroup')}>
27+
<span className={variants('groupLabel')}>{group}</span>
28+
<div className={variants('contentGrid')}>
29+
{Object.entries(items).map(([name, spec]) => {
30+
switch (spec.type) {
31+
case 'array':
32+
return <PropertyControlArray key={name} name={name} spec={spec} />;
33+
case 'boolean':
34+
return <PropertyControlBoolean key={name} name={name} spec={spec} />;
35+
case 'custom':
36+
return <PropertyControlCustom key={name} name={name} spec={spec} />;
37+
case 'node':
38+
return <PropertyControlNode key={name} name={name} spec={spec} />;
39+
case 'number':
40+
return <PropertyControlNumber key={name} name={name} spec={spec} />;
41+
case 'object':
42+
return <PropertyControlObject key={name} name={name} spec={spec} />;
43+
case 'radio':
44+
return <PropertyControlRadio key={name} name={name} spec={spec} />;
45+
case 'select':
46+
return <PropertyControlSelect key={name} name={name} spec={spec} />;
47+
case 'text':
48+
return <PropertyControlText key={name} name={name} spec={spec} />;
49+
case 'textarea':
50+
return <PropertyControlTextArea key={name} name={name} spec={spec} />;
51+
default:
52+
return null;
53+
}
54+
})}
55+
</div>
56+
</VStack>
57+
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { PropertyGroup } from './PropertyGroup';
Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,7 @@
11
import type { FC } from 'react';
22
import { Catalog } from '../../renderer';
3-
import { getClassNameFactory } from '../../utils';
43
import { useEditing } from '../EditingContext';
5-
import { PropertyControlArray } from '../PropertyControlArray';
6-
import { PropertyControlBoolean } from '../PropertyControlBoolean';
7-
import { PropertyControlCustom } from '../PropertyControlCustom';
8-
import { PropertyControlNode } from '../PropertyControlNode';
9-
import { PropertyControlNumber } from '../PropertyControlNumber';
10-
import { PropertyControlObject } from '../PropertyControlObject';
11-
import { PropertyControlRadio } from '../PropertyControlRadio';
12-
import { PropertyControlSelect } from '../PropertyControlSelect';
13-
import { PropertyControlText } from '../PropertyControlText';
14-
import { PropertyControlTextArea } from '../PropertyControlTextArea';
15-
import styles from './PropertyLibrary.module.css';
16-
17-
const getClassName = getClassNameFactory('PropertyLibrary', styles);
4+
import { PropertyGroup } from '../PropertyGroup';
185

196
export const PropertyLibrary: FC<unknown> = () => {
207
const { activeBlock } = useEditing();
@@ -29,34 +16,7 @@ export const PropertyLibrary: FC<unknown> = () => {
2916
return null;
3017
}
3118

32-
return (
33-
<div className={getClassName()}>
34-
{Object.entries(block.props).map(([name, spec]) => {
35-
switch (spec.type) {
36-
case 'array':
37-
return <PropertyControlArray key={name} name={name} spec={spec} />;
38-
case 'boolean':
39-
return <PropertyControlBoolean key={name} name={name} spec={spec} />;
40-
case 'custom':
41-
return <PropertyControlCustom key={name} name={name} spec={spec} />;
42-
case 'node':
43-
return <PropertyControlNode key={name} name={name} spec={spec} />;
44-
case 'number':
45-
return <PropertyControlNumber key={name} name={name} spec={spec} />;
46-
case 'object':
47-
return <PropertyControlObject key={name} name={name} spec={spec} />;
48-
case 'radio':
49-
return <PropertyControlRadio key={name} name={name} spec={spec} />;
50-
case 'select':
51-
return <PropertyControlSelect key={name} name={name} spec={spec} />;
52-
case 'text':
53-
return <PropertyControlText key={name} name={name} spec={spec} />;
54-
case 'textarea':
55-
return <PropertyControlTextArea key={name} name={name} spec={spec} />;
56-
default:
57-
return null;
58-
}
59-
})}
60-
</div>
61-
);
19+
return Object.entries(block.getGroupedProps()).map(([group, props]) => (
20+
<PropertyGroup key={group} group={group} items={props} />
21+
));
6222
};

packages/react/src/preset/HStack/HStackCatalog.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Catalog.register('HStack', {
1616
component: HStack,
1717
props: {
1818
alignHorizontal: {
19+
group: 'Layout',
1920
label: 'Align',
2021
type: 'radio',
2122
options: [
@@ -28,6 +29,7 @@ Catalog.register('HStack', {
2829
default: 'start',
2930
},
3031
alignVertical: {
32+
group: 'Layout',
3133
label: 'Distribute',
3234
type: 'radio',
3335
options: [
@@ -39,11 +41,13 @@ Catalog.register('HStack', {
3941
default: 'stretch',
4042
},
4143
flex: {
44+
group: 'Layout',
4245
label: 'Flex',
4346
type: 'number',
4447
optional: true,
4548
},
4649
gap: {
50+
group: 'Layout',
4751
label: 'Gap',
4852
type: 'number',
4953
optional: true,
@@ -98,6 +102,7 @@ Catalog.register('HStack', {
98102
optional: true,
99103
},
100104
padding: {
105+
group: 'Layout',
101106
label: 'Padding',
102107
type: 'object',
103108
fields: {
@@ -125,6 +130,7 @@ Catalog.register('HStack', {
125130
optional: true,
126131
},
127132
margin: {
133+
group: 'Layout',
128134
label: 'Margin',
129135
type: 'object',
130136
fields: {
Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ReactNode, useEffect, useState } from 'react';
1+
import type { ReactNode } from 'react';
22
import { createVariants } from '../../utils';
33
import styles from './Segment.module.css';
44

@@ -15,25 +15,24 @@ type Props<Value> = {
1515

1616
const variants = createVariants(styles);
1717

18-
export const Segment = <Value,>({ className, size, options, onChange, ...props }: Props<Value>) => {
19-
const [value, setValue] = useState(options[0]?.value);
20-
21-
useEffect(() => {
22-
onChange?.(value);
23-
}, [value, onChange]);
24-
25-
return (
26-
<div className={variants('segment', { className })} {...props}>
27-
{options.map((option) => (
28-
<button
29-
type="button"
30-
key={String(option.value)}
31-
className={variants('option', { size, active: option.value === value })}
32-
onClick={() => setValue(option.value)}
33-
>
34-
{option.label}
35-
</button>
36-
))}
37-
</div>
38-
);
39-
};
18+
export const Segment = <Value,>({
19+
className,
20+
size,
21+
options,
22+
value,
23+
onChange,
24+
...props
25+
}: Props<Value>) => (
26+
<div className={variants('segment', { className })} {...props}>
27+
{options.map((option) => (
28+
<button
29+
type="button"
30+
key={String(option.value)}
31+
className={variants('option', { size, active: option.value === value })}
32+
onClick={() => onChange(option.value)}
33+
>
34+
{option.label}
35+
</button>
36+
))}
37+
</div>
38+
);

0 commit comments

Comments
 (0)