refactor(components): unify accessory prop handling for UI components

This refactoring introduces a new `createSlottedComponent` factory to abstract the repetitive logic for handling "accessory" props like `actions`, `detail`, and `searchBarAccessory`. It replacs the manual, boilerplate implementations in `List`, `Grid`, `Form`, and `Detail` components with a single call to this new factory.
This commit is contained in:
ByteAtATime 2025-07-04 21:25:45 -07:00
parent a3fe9b6df7
commit 6ed4af5ce7
No known key found for this signature in database
5 changed files with 28 additions and 76 deletions

View file

@ -1,18 +1,6 @@
import { jsx } from 'react/jsx-runtime';
import { createWrapperComponent, createAccessorySlot } from '../utils';
import { createWrapperComponent, createSlottedComponent } from '../utils';
const _AccessorySlot = createAccessorySlot();
const DetailPrimitive = createWrapperComponent('Detail');
const Detail = (props) => {
const { metadata, actions, children, ...rest } = props;
const metadataElement = metadata && jsx(_AccessorySlot, { name: 'metadata', children: metadata });
const actionsElement = actions && jsx(_AccessorySlot, { name: 'actions', children: actions });
return jsx(DetailPrimitive, {
...rest,
children: [children, metadataElement, actionsElement].filter(Boolean)
});
};
const Detail = createSlottedComponent('Detail', ['metadata', 'actions']);
const DetailMetadata = createWrapperComponent('Detail.Metadata');
const DetailMetadataLabel = createWrapperComponent('Detail.Metadata.Label');

View file

@ -1,20 +1,6 @@
import { jsx } from 'react/jsx-runtime';
import { createWrapperComponent, createAccessorySlot } from '../utils';
import { createWrapperComponent, createSlottedComponent } from '../utils';
const _AccessorySlot = createAccessorySlot();
const FormPrimitive = createWrapperComponent('Form');
const Form = (props) => {
const { actions, children, searchBarAccessory, ...rest } = props;
const accessoryElement =
searchBarAccessory &&
jsx(_AccessorySlot, { name: 'searchBarAccessory', children: searchBarAccessory });
const actionsElement = actions && jsx(_AccessorySlot, { name: 'actions', children: actions });
return jsx(FormPrimitive, {
...rest,
children: [children, accessoryElement, actionsElement].filter(Boolean)
});
};
const Form = createSlottedComponent('Form', ['searchBarAccessory', 'actions']);
const FormTextField = createWrapperComponent('Form.TextField');
const FormTextArea = createWrapperComponent('Form.TextArea');

View file

@ -1,27 +1,7 @@
import { jsx } from 'react/jsx-runtime';
import { createWrapperComponent, createAccessorySlot } from '../utils';
import { createWrapperComponent, createSlottedComponent } from '../utils';
const _AccessorySlot = createAccessorySlot();
const GridPrimitive = createWrapperComponent('Grid');
const Grid = (props) => {
const { searchBarAccessory, children, ...rest } = props;
const accessoryElement =
searchBarAccessory &&
jsx(_AccessorySlot, { name: 'searchBarAccessory', children: searchBarAccessory });
return jsx(GridPrimitive, { ...rest, children: [children, accessoryElement].filter(Boolean) });
};
const GridItemPrimitive = createWrapperComponent('Grid.Item');
const GridItem = (props) => {
const { detail, actions, children, ...rest } = props;
const detailElement = detail && jsx(_AccessorySlot, { name: 'detail', children: detail });
const actionsElement = actions && jsx(_AccessorySlot, { name: 'actions', children: actions });
return jsx(GridItemPrimitive, {
...rest,
children: [children, detailElement, actionsElement].filter(Boolean)
});
};
const Grid = createSlottedComponent('Grid', ['searchBarAccessory']);
const GridItem = createSlottedComponent('Grid.Item', ['detail', 'actions']);
const GridSection = createWrapperComponent('Grid.Section');
const GridDropdown = createWrapperComponent('Grid.Dropdown');

View file

@ -1,27 +1,7 @@
import { jsx } from 'react/jsx-runtime';
import { createWrapperComponent, createAccessorySlot } from '../utils';
import { createWrapperComponent, createSlottedComponent } from '../utils';
const _AccessorySlot = createAccessorySlot();
const ListPrimitive = createWrapperComponent('List');
const List = (props) => {
const { searchBarAccessory, children, ...rest } = props;
const accessoryElement =
searchBarAccessory &&
jsx(_AccessorySlot, { name: 'searchBarAccessory', children: searchBarAccessory });
return jsx(ListPrimitive, { ...rest, children: [children, accessoryElement].filter(Boolean) });
};
const ListItemPrimitive = createWrapperComponent('List.Item');
const ListItem = (props) => {
const { detail, actions, children, ...rest } = props;
const detailElement = detail && jsx(_AccessorySlot, { name: 'detail', children: detail });
const actionsElement = actions && jsx(_AccessorySlot, { name: 'actions', children: actions });
return jsx(ListItemPrimitive, {
...rest,
children: [children, detailElement, actionsElement].filter(Boolean)
});
};
const List = createSlottedComponent('List', ['searchBarAccessory']);
const ListItem = createSlottedComponent('List.Item', ['detail', 'actions']);
const ListSection = createWrapperComponent('List.Section');
const ListEmptyView = createWrapperComponent('List.EmptyView');

View file

@ -22,3 +22,21 @@ export const createWrapperComponent = (name: string) => {
};
export const createAccessorySlot = () => createWrapperComponent('_AccessorySlot');
export const createSlottedComponent = (baseName: string, accessoryPropNames: string[]) => {
const _AccessorySlot = createAccessorySlot();
const Primitive = createWrapperComponent(baseName);
const SlottedComponent = (props: { [key: string]: any; children?: React.ReactNode }) => {
const { children, ...rest } = props;
const accessoryElements = [];
for (const name of accessoryPropNames) {
if (rest[name]) {
accessoryElements.push(jsx(_AccessorySlot, { name, children: rest[name] }));
delete rest[name];
}
}
return jsx(Primitive, { ...rest, children: [children, ...accessoryElements].filter(Boolean) });
};
SlottedComponent.displayName = baseName;
return SlottedComponent;
};