Reorganize twenty-ui into best-practice component domains and per-component folders (#21745)
Reorganizes `twenty-ui`'s component organization to follow how the best
UI libraries (MUI, Mantine, Base UI, Polaris) structure their source,
now that the package has stabilized.
**Taxonomy** — dissolves the meaningless `components/` junk-drawer and
the 107-file `display/` mega-category. New domains/subpaths:
`data-display`, `typography`, `icon`, `surfaces`; `feedback` and
`layout` absorb the rest (banners/callout/info + placeholders →
feedback; modal/card → surfaces; motion + separators → layout).
**Per-component layout** — every component is now
`<domain>/<ComponentName>/<ComponentName>.tsx` with colocated
styles/stories/types, `internal/` for private helpers and `parts/` for
re-exported compound sub-parts. The redundant inner `/components/` is
gone. `icon` and `json-visualizer` are kept as cohesive subsystems.
**Also:** adds a tree-shakeable root barrel (`import { Button } from
'twenty-ui'`), the generator now owns `individual-entry.ts`, and a real
barrel-leak bug is fixed (private `internals/` parts were leaking into
the public API).
Consumer imports (~1.2k files) and the `twenty-sdk` UI aggregator were
updated by codemod. The change is **export-neutral** except 16
intentionally-removed private internals symbols (all verified
unconsumed). Gates green: typecheck, lint, build, size-limit, storybook.
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21745?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { motion, useAnimation } from 'framer-motion';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
type CircularProgressBarProps = {
|
||||
size?: number;
|
||||
barWidth?: number;
|
||||
barColor?: string;
|
||||
};
|
||||
|
||||
export const CircularProgressBar = ({
|
||||
size = 50,
|
||||
barWidth = 5,
|
||||
barColor = 'currentColor',
|
||||
}: CircularProgressBarProps) => {
|
||||
const controls = useAnimation();
|
||||
|
||||
const circumference = useMemo(
|
||||
() => 2 * Math.PI * (size / 2 - barWidth),
|
||||
[size, barWidth],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const animateIndeterminate = async () => {
|
||||
const baseSegment = Math.max(5, circumference / 10); // Adjusting for smaller values
|
||||
|
||||
// Adjusted sequence based on baseSegment
|
||||
const dashSequences = [
|
||||
`${baseSegment} ${circumference - baseSegment}`,
|
||||
`${baseSegment * 2} ${circumference - baseSegment * 2}`,
|
||||
`${baseSegment * 3} ${circumference - baseSegment * 3}`,
|
||||
`${baseSegment * 2} ${circumference - baseSegment * 2}`,
|
||||
`${baseSegment} ${circumference - baseSegment}`,
|
||||
];
|
||||
|
||||
await controls.start({
|
||||
strokeDasharray: dashSequences,
|
||||
rotate: [0, 720],
|
||||
transition: {
|
||||
strokeDasharray: {
|
||||
duration: 2,
|
||||
ease: 'linear',
|
||||
repeat: Infinity,
|
||||
repeatType: 'loop',
|
||||
},
|
||||
rotate: {
|
||||
duration: 2,
|
||||
ease: 'linear',
|
||||
repeat: Infinity,
|
||||
repeatType: 'loop',
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
animateIndeterminate();
|
||||
}, [circumference, controls]);
|
||||
|
||||
return (
|
||||
<motion.svg width={size} height={size} animate={controls}>
|
||||
<motion.circle
|
||||
cx={size / 2}
|
||||
cy={size / 2}
|
||||
r={size / 2 - barWidth}
|
||||
fill="none"
|
||||
stroke={barColor}
|
||||
strokeWidth={barWidth}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</motion.svg>
|
||||
);
|
||||
};
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { CatalogDecorator, type CatalogStory } from '@ui/testing';
|
||||
|
||||
import { ComponentDecorator } from '@ui/testing/decorators/ComponentDecorator';
|
||||
import { CircularProgressBar } from '@ui/feedback/CircularProgressBar/CircularProgressBar';
|
||||
|
||||
const meta: Meta<typeof CircularProgressBar> = {
|
||||
title: 'UI/Feedback/CircularProgressBar/CircularProgressBar',
|
||||
component: CircularProgressBar,
|
||||
args: {
|
||||
size: 50,
|
||||
},
|
||||
parameters: {
|
||||
chromatic: { disableSnapshot: true },
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CircularProgressBar>;
|
||||
|
||||
export const Default: Story = {
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export const Catalog: CatalogStory<Story, typeof CircularProgressBar> = {
|
||||
argTypes: {},
|
||||
parameters: {
|
||||
// TODO(a11y): violations inherited from deprecated story; fix during a11y pass
|
||||
a11y: { test: 'todo' },
|
||||
catalog: {
|
||||
dimensions: [
|
||||
{
|
||||
name: 'barColor',
|
||||
values: [undefined, 'red'],
|
||||
props: (barColor: string) => ({ barColor }),
|
||||
labels: (color: string) => `Segment Color: ${color ?? 'default'}`,
|
||||
},
|
||||
{
|
||||
name: 'barWidth',
|
||||
values: [undefined, 5, 10],
|
||||
props: (barWidth: number) => ({ barWidth }),
|
||||
labels: (width: number) =>
|
||||
`Stroke Width: ${width ? width + ' px' : 'default'}`,
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
values: [undefined, 80, 30],
|
||||
props: (size: number) => ({ size }),
|
||||
labels: (size: number) => `Size: ${size ? size + ' px' : 'default'}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
decorators: [CatalogDecorator],
|
||||
};
|
||||
Reference in New Issue
Block a user