[FRONT COMPONENTS] Twenty UI elements generation (#17866)

## PR description

This PR:
- Creates a TypeScript-based extractor that discovers all exported
twenty-ui components by scanning barrel files, extracting
props/slots/events via ts-morph type analysis, and generating the remote
DOM bindings automatically.

- Adds a new ESLint rule which enforces all *Props types in twenty-ui
components to be exported, which is required for the extractor to
discover component prop types. Existing twenty-ui components are updated
to comply with this rule.

- Extends the remote DOM generation to support slots, per-component
events, forwardRef wrappers, and richer property types (array, object,
function)

## Edge cases to fix in another PR

- Icons cannot be rendered inside buttons
- IconButtons throw an error when mounted
- MenuItems are not displayed correctly
- MenuItemNavigate throws on click

## Video Demo


https://github.com/user-attachments/assets/c2ed67cf-6a15-4896-9fec-e83fac0e862b
This commit is contained in:
Raphaël Bosi
2026-02-12 12:48:16 +01:00
committed by GitHub
parent c48ccbca99
commit cb7d0b83a8
133 changed files with 10556 additions and 984 deletions
@@ -23,7 +23,7 @@ const StyledRadioContainer = styled.div`
top: ${({ theme }) => theme.spacing(2)};
`;
type CardPickerProps = {
export type CardPickerProps = {
children: React.ReactNode;
handleChange?: () => void;
checked?: boolean;
@@ -24,7 +24,7 @@ export enum CheckboxAccent {
Orange = 'orange',
}
type CheckboxProps = {
export type CheckboxProps = {
checked: boolean;
indeterminate?: boolean;
hoverable?: boolean;
@@ -38,7 +38,7 @@ type CheckboxProps = {
accent?: CheckboxAccent;
};
type InputProps = {
export type InputProps = {
checkboxSize: CheckboxSize;
variant: CheckboxVariant;
accent?: CheckboxAccent;
@@ -1,7 +1,7 @@
import IconListViewGripRaw from '@assets/misc/list-view-grip.svg?react';
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
type IconListViewGripProps = Pick<IconComponentProps, 'size' | 'stroke'>;
export type IconListViewGripProps = Pick<IconComponentProps, 'size' | 'stroke'>;
export const IconListViewGrip = (props: IconListViewGripProps) => {
const width = props.size ?? 8;
@@ -3,7 +3,7 @@ import { useTheme } from '@emotion/react';
import { type RadioProps } from './Radio';
type RadioGroupProps = React.PropsWithChildren & {
export type RadioGroupProps = React.PropsWithChildren & {
value?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
onValueChange?: (value: string) => void;