@particle-academy/react-fancy 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -16
- package/dist/diagram.serializers-OK4HP7AB.js +273 -0
- package/dist/diagram.serializers-OK4HP7AB.js.map +1 -0
- package/dist/index.cjs +7489 -329
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1681 -34
- package/dist/index.d.ts +1681 -34
- package/dist/index.js +7106 -326
- package/dist/index.js.map +1 -1
- package/dist/styles.css +116 -0
- package/dist/styles.css.map +1 -1
- package/package.json +13 -11
package/README.md
CHANGED
|
@@ -10,10 +10,12 @@ pnpm add @particle-academy/react-fancy
|
|
|
10
10
|
|
|
11
11
|
Peer dependencies: `react >= 18`, `react-dom >= 18`.
|
|
12
12
|
|
|
13
|
+
Dependencies: `lucide-react` (default icon library, bundled externally).
|
|
14
|
+
|
|
13
15
|
## Usage
|
|
14
16
|
|
|
15
17
|
```tsx
|
|
16
|
-
import { Action, Input,
|
|
18
|
+
import { Action, Input, Modal, Dropdown } from "@particle-academy/react-fancy";
|
|
17
19
|
import "@particle-academy/react-fancy/styles.css";
|
|
18
20
|
```
|
|
19
21
|
|
|
@@ -34,14 +36,21 @@ npx vite build # Build demo app (verifies imports work)
|
|
|
34
36
|
|
|
35
37
|
## Components
|
|
36
38
|
|
|
39
|
+
### Core
|
|
40
|
+
|
|
37
41
|
| Component | Description |
|
|
38
42
|
|-----------|-------------|
|
|
39
43
|
| Action | Button with colors, states, icons, emoji, avatar, badge, sort control |
|
|
40
|
-
| Carousel | Slide carousel with
|
|
41
|
-
| ColorPicker |
|
|
44
|
+
| Carousel | Slide carousel with directional/wizard variants, autoplay, loop |
|
|
45
|
+
| ColorPicker | Native color input with swatch preview, hex display, presets |
|
|
42
46
|
| Emoji | Emoji renderer from slugs |
|
|
43
47
|
| EmojiSelect | Emoji search and selection dropdown |
|
|
44
48
|
| Table | Data table with sorting, pagination, search, and tray |
|
|
49
|
+
|
|
50
|
+
### Form Inputs
|
|
51
|
+
|
|
52
|
+
| Component | Description |
|
|
53
|
+
|-----------|-------------|
|
|
45
54
|
| Field | Form field wrapper with label and error display |
|
|
46
55
|
| Input | Text input |
|
|
47
56
|
| Textarea | Multi-line text input |
|
|
@@ -51,6 +60,93 @@ npx vite build # Build demo app (verifies imports work)
|
|
|
51
60
|
| Switch | Toggle switch |
|
|
52
61
|
| Slider | Range slider (single and range modes) |
|
|
53
62
|
| DatePicker | Date selection (single and range modes) |
|
|
63
|
+
| Autocomplete | Input with filtered dropdown suggestions, async search, keyboard nav |
|
|
64
|
+
| Pillbox | Tag/pill input with add/remove, backspace delete |
|
|
65
|
+
| OtpInput | Single-digit OTP code input with auto-advance and paste support |
|
|
66
|
+
| FileUpload | Drag-and-drop file upload with dropzone and file list |
|
|
67
|
+
| TimePicker | Hour/minute/AM-PM time selection |
|
|
68
|
+
| Calendar | Month grid with single, range, and multi-select modes |
|
|
69
|
+
|
|
70
|
+
### Display
|
|
71
|
+
|
|
72
|
+
| Component | Description |
|
|
73
|
+
|-----------|-------------|
|
|
74
|
+
| Heading | Semantic heading (`h1`–`h6`) with size and weight props |
|
|
75
|
+
| Text | Paragraph/span with size, color, weight, and `as` prop |
|
|
76
|
+
| Separator | Horizontal/vertical divider with optional label |
|
|
77
|
+
| Badge | Inline label with color, variant, size, and dot indicator |
|
|
78
|
+
| Icon | Size wrapper around icon ReactNode |
|
|
79
|
+
| Avatar | Image with fallback initials, size variants, status indicator |
|
|
80
|
+
| Skeleton | Animated placeholder (rect, circle, text), pulse animation |
|
|
81
|
+
| Progress | Bar and circular variants, indeterminate mode |
|
|
82
|
+
| Brand | Logo + text lockup |
|
|
83
|
+
| Profile | Avatar + name + subtitle layout |
|
|
84
|
+
| Card | Container with Header, Body, Footer compound slots |
|
|
85
|
+
| Callout | Alert/info box with icon, color, and dismissible support |
|
|
86
|
+
| Timeline | Vertical event list with Item and Block sub-components |
|
|
87
|
+
|
|
88
|
+
### Overlay & Floating
|
|
89
|
+
|
|
90
|
+
| Component | Description |
|
|
91
|
+
|-----------|-------------|
|
|
92
|
+
| Tooltip | Hover/focus tooltip with arrow and placement control |
|
|
93
|
+
| Popover | Click-triggered floating panel |
|
|
94
|
+
| Dropdown | Popover with keyboard-navigable menu items |
|
|
95
|
+
| ContextMenu | Right-click triggered dropdown |
|
|
96
|
+
| Modal | Full-screen backdrop dialog with focus trap and scroll lock |
|
|
97
|
+
| Toast | Notification stack with auto-dismiss, variants, and position options |
|
|
98
|
+
| Command | `Cmd+K` command palette with search and keyboard navigation |
|
|
99
|
+
|
|
100
|
+
### Navigation & Layout
|
|
101
|
+
|
|
102
|
+
| Component | Description |
|
|
103
|
+
|-----------|-------------|
|
|
104
|
+
| Tabs | Tabbed content with underline, pills, and boxed variants |
|
|
105
|
+
| Accordion | Collapsible content sections (single/multiple mode) |
|
|
106
|
+
| Breadcrumbs | Navigation breadcrumb trail with separator |
|
|
107
|
+
| Navbar | Responsive navigation bar with hamburger collapse |
|
|
108
|
+
| Pagination | Page navigation with prev/next and ellipsis |
|
|
109
|
+
|
|
110
|
+
### Rich Content
|
|
111
|
+
|
|
112
|
+
| Component | Description |
|
|
113
|
+
|-----------|-------------|
|
|
114
|
+
| Composer | Chat-style message input composing textarea + actions |
|
|
115
|
+
| Chart | SVG-based Bar and Donut charts |
|
|
116
|
+
| Editor | Toolbar chrome wrapper for contentEditable |
|
|
117
|
+
| Kanban | Drag-and-drop board with columns and cards (experimental) |
|
|
118
|
+
|
|
119
|
+
### Utilities & Hooks
|
|
120
|
+
|
|
121
|
+
| Export | Description |
|
|
122
|
+
|--------|-------------|
|
|
123
|
+
| Portal | `createPortal` wrapper with automatic dark mode propagation |
|
|
124
|
+
| `cn()` | `clsx` + `tailwind-merge` for conditional class composition |
|
|
125
|
+
| `useControllableState` | Controlled/uncontrolled state management |
|
|
126
|
+
| `useFloatingPosition` | Anchor-relative positioning for floating elements |
|
|
127
|
+
| `useOutsideClick` | Close on click outside handler |
|
|
128
|
+
| `useEscapeKey` | Close on Escape key handler |
|
|
129
|
+
| `useFocusTrap` | Tab-cycle focus within container |
|
|
130
|
+
| `useAnimation` | Enter/exit CSS transitions with unmount |
|
|
131
|
+
| `useId` | Stable ID generation |
|
|
132
|
+
|
|
133
|
+
## Customization
|
|
134
|
+
|
|
135
|
+
All components render a `data-react-fancy-*` attribute on their root element (e.g., `data-react-fancy-modal`, `data-react-fancy-dropdown-item`). Use these for external CSS targeting or JavaScript integration:
|
|
136
|
+
|
|
137
|
+
```css
|
|
138
|
+
[data-react-fancy-modal] {
|
|
139
|
+
--custom-border-radius: 1rem;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
document.querySelectorAll("[data-react-fancy-dropdown-item]");
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Dark Mode
|
|
148
|
+
|
|
149
|
+
Dark mode works via Tailwind's `dark:` class strategy. The library's `Portal` component automatically detects the `dark` class (or `data-theme="dark"`) on `<html>` and propagates it into portaled content (modals, dropdowns, tooltips, toasts, etc.).
|
|
54
150
|
|
|
55
151
|
## Architecture
|
|
56
152
|
|
|
@@ -63,27 +159,30 @@ src/
|
|
|
63
159
|
│ │ ├── Action.tsx # Component implementation
|
|
64
160
|
│ │ ├── Action.types.ts # Props interface
|
|
65
161
|
│ │ └── index.ts # Re-exports
|
|
66
|
-
│ ├──
|
|
162
|
+
│ ├── Modal/
|
|
163
|
+
│ │ ├── Modal.tsx # Root + Object.assign compound
|
|
164
|
+
│ │ ├── Modal.context.ts # React context (compound components)
|
|
165
|
+
│ │ ├── Modal.types.ts # Props interfaces
|
|
166
|
+
│ │ ├── ModalHeader.tsx # Sub-component
|
|
167
|
+
│ │ ├── ModalBody.tsx
|
|
168
|
+
│ │ ├── ModalFooter.tsx
|
|
169
|
+
│ │ └── index.ts
|
|
67
170
|
│ ├── inputs/ # Form input components (Field, Input, Select, etc.)
|
|
68
171
|
│ └── ...
|
|
69
172
|
├── data/ # Static data (emoji entries, etc.)
|
|
70
173
|
├── hooks/ # Shared React hooks
|
|
71
174
|
├── utils/ # Shared utilities (cn, types)
|
|
175
|
+
├── styles.css # Keyframe animations
|
|
72
176
|
└── index.ts # Public API — all exports
|
|
73
177
|
```
|
|
74
178
|
|
|
75
|
-
### Key Utilities
|
|
76
|
-
|
|
77
|
-
- **`cn()`** (`utils/cn.ts`) — `clsx` + `tailwind-merge` for conditional class composition.
|
|
78
|
-
- **`resolve(slug)`** (`data/emoji-utils.ts`) — Resolves emoji slugs (e.g., `"rocket"`) to Unicode characters.
|
|
79
|
-
- **`useControllableState`** (`hooks/`) — For components supporting both controlled and uncontrolled modes.
|
|
80
|
-
|
|
81
179
|
### Shared Types (`utils/types.ts`)
|
|
82
180
|
|
|
83
181
|
- `Size` — `"xs" | "sm" | "md" | "lg" | "xl"`
|
|
84
182
|
- `Color` — Full Tailwind color palette (17 colors)
|
|
85
183
|
- `ActionColor` — Subset of 10 standalone colors matching fancy-flux
|
|
86
184
|
- `Variant` — `"solid" | "outline" | "ghost" | "soft"`
|
|
185
|
+
- `Placement` — `"top" | "bottom" | "left" | "right"` + start/end variants
|
|
87
186
|
|
|
88
187
|
## Demo Pages
|
|
89
188
|
|
|
@@ -100,9 +199,14 @@ Guidelines for AI agents (Claude Code, Copilot, etc.) working on this package.
|
|
|
100
199
|
Every component follows this structure:
|
|
101
200
|
|
|
102
201
|
1. **`ComponentName.types.ts`** — Props interface extending native HTML element attributes. Import shared types from `../../utils/types`.
|
|
103
|
-
2. **`ComponentName.tsx`** — Implementation using `forwardRef`. Always set `displayName`. Use `cn()` for class merging.
|
|
104
|
-
3.
|
|
105
|
-
4. **`
|
|
202
|
+
2. **`ComponentName.tsx`** — Implementation using `forwardRef`. Always set `displayName`. Use `cn()` for class merging. Add `data-react-fancy-{name}=""` to the root element.
|
|
203
|
+
3. **Compound components** — Use `Object.assign(Root, { Sub1, Sub2 })` pattern. Add a `.context.ts` with React context. Each sub-component gets its own `data-react-fancy-{parent}-{sub}` attribute.
|
|
204
|
+
4. **`index.ts`** — Re-exports both the component and its types.
|
|
205
|
+
5. **`src/index.ts`** — Must export the component and its prop types. Update this file when adding new components.
|
|
206
|
+
|
|
207
|
+
### Icons
|
|
208
|
+
|
|
209
|
+
Use `lucide-react` as the default icon library. It is a dependency of this package and marked as external in tsup. Components should import icons directly (e.g., `import { X, ChevronDown } from "lucide-react"`).
|
|
106
210
|
|
|
107
211
|
### Parity with fancy-flux
|
|
108
212
|
|
|
@@ -113,8 +217,8 @@ Every component follows this structure:
|
|
|
113
217
|
### Styling
|
|
114
218
|
|
|
115
219
|
- **Tailwind v4** — CSS-first config. Use `@import "tailwindcss"` not `@tailwind` directives.
|
|
116
|
-
- **Dark mode** — Every color variant must include `dark:` equivalents. Check fancy-flux for the exact classes.
|
|
117
|
-
- **No component library deps** — Only `clsx` and `
|
|
220
|
+
- **Dark mode** — Every color variant must include `dark:` equivalents. Check fancy-flux for the exact classes. Portal components get dark mode automatically via the Portal wrapper.
|
|
221
|
+
- **No component library deps** — Only `clsx`, `tailwind-merge`, and `lucide-react`. Don't add Radix, Headless UI, or similar.
|
|
118
222
|
- Class maps should be `Record<Size, string>` (or similar) constants outside the component function, not inline.
|
|
119
223
|
|
|
120
224
|
### TypeScript
|
|
@@ -126,6 +230,6 @@ Every component follows this structure:
|
|
|
126
230
|
### Build
|
|
127
231
|
|
|
128
232
|
- tsup handles the build — ESM, CJS, and `.d.ts` generation.
|
|
129
|
-
- `react` and `react
|
|
233
|
+
- `react`, `react-dom`, and `lucide-react` are external dependencies, never bundled.
|
|
130
234
|
- After any change, verify with `pnpm --filter @particle-academy/react-fancy build` before considering the work done.
|
|
131
235
|
- When updating a component, update its demo page in `resources/js/react-demos/pages/` to cover all new features.
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// src/components/Diagram/diagram.serializers.ts
|
|
2
|
+
function serializeToERD(schema) {
|
|
3
|
+
const lines = [];
|
|
4
|
+
for (const entity of schema.entities) {
|
|
5
|
+
lines.push(`[${entity.name}]`);
|
|
6
|
+
if (entity.fields) {
|
|
7
|
+
for (const field of entity.fields) {
|
|
8
|
+
const parts = [` ${field.name}`];
|
|
9
|
+
if (field.type) parts.push(field.type);
|
|
10
|
+
if (field.primary) parts.push("PK");
|
|
11
|
+
if (field.foreign) parts.push("FK");
|
|
12
|
+
if (field.nullable) parts.push("?");
|
|
13
|
+
lines.push(parts.join(" "));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
lines.push("");
|
|
17
|
+
}
|
|
18
|
+
for (const rel of schema.relations) {
|
|
19
|
+
const fromEntity = schema.entities.find((e) => e.id === rel.from);
|
|
20
|
+
const toEntity = schema.entities.find((e) => e.id === rel.to);
|
|
21
|
+
if (!fromEntity || !toEntity) continue;
|
|
22
|
+
const marker = getERDMarker(rel.type);
|
|
23
|
+
const parts = [fromEntity.name, marker, toEntity.name];
|
|
24
|
+
if (rel.label) parts.push(`: ${rel.label}`);
|
|
25
|
+
lines.push(parts.join(" "));
|
|
26
|
+
}
|
|
27
|
+
return lines.join("\n").trim();
|
|
28
|
+
}
|
|
29
|
+
function getERDMarker(type) {
|
|
30
|
+
switch (type) {
|
|
31
|
+
case "one-to-one":
|
|
32
|
+
return "1--1";
|
|
33
|
+
case "one-to-many":
|
|
34
|
+
return "1--*";
|
|
35
|
+
case "many-to-many":
|
|
36
|
+
return "*--*";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function serializeToUML(schema) {
|
|
40
|
+
const lines = ["@startuml"];
|
|
41
|
+
for (const entity of schema.entities) {
|
|
42
|
+
lines.push(`class ${entity.name} {`);
|
|
43
|
+
if (entity.fields) {
|
|
44
|
+
for (const field of entity.fields) {
|
|
45
|
+
const typeStr = field.type ?? "any";
|
|
46
|
+
const nullable = field.nullable ? "?" : "";
|
|
47
|
+
const stereotype = field.primary ? " <<PK>>" : field.foreign ? " <<FK>>" : "";
|
|
48
|
+
lines.push(` ${field.name} : ${typeStr}${nullable}${stereotype}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
lines.push("}");
|
|
52
|
+
lines.push("");
|
|
53
|
+
}
|
|
54
|
+
for (const rel of schema.relations) {
|
|
55
|
+
const fromEntity = schema.entities.find((e) => e.id === rel.from);
|
|
56
|
+
const toEntity = schema.entities.find((e) => e.id === rel.to);
|
|
57
|
+
if (!fromEntity || !toEntity) continue;
|
|
58
|
+
const arrow = getUMLArrow(rel.type);
|
|
59
|
+
const label = rel.label ? ` : ${rel.label}` : "";
|
|
60
|
+
lines.push(`${fromEntity.name} ${arrow} ${toEntity.name}${label}`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("@enduml");
|
|
63
|
+
return lines.join("\n");
|
|
64
|
+
}
|
|
65
|
+
function getUMLArrow(type) {
|
|
66
|
+
switch (type) {
|
|
67
|
+
case "one-to-one":
|
|
68
|
+
return '"1" -- "1"';
|
|
69
|
+
case "one-to-many":
|
|
70
|
+
return '"1" -- "*"';
|
|
71
|
+
case "many-to-many":
|
|
72
|
+
return '"*" -- "*"';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function serializeToDFD(schema) {
|
|
76
|
+
const lines = [];
|
|
77
|
+
for (const entity of schema.entities) {
|
|
78
|
+
lines.push(`entity ${entity.name}`);
|
|
79
|
+
}
|
|
80
|
+
lines.push("");
|
|
81
|
+
for (const rel of schema.relations) {
|
|
82
|
+
const fromEntity = schema.entities.find((e) => e.id === rel.from);
|
|
83
|
+
const toEntity = schema.entities.find((e) => e.id === rel.to);
|
|
84
|
+
if (!fromEntity || !toEntity) continue;
|
|
85
|
+
const label = rel.label ? ` "${rel.label}"` : "";
|
|
86
|
+
lines.push(`${fromEntity.name} -> ${toEntity.name}${label}`);
|
|
87
|
+
}
|
|
88
|
+
return lines.join("\n").trim();
|
|
89
|
+
}
|
|
90
|
+
function deserializeSchema(input, format) {
|
|
91
|
+
switch (format) {
|
|
92
|
+
case "erd":
|
|
93
|
+
return deserializeERD(input);
|
|
94
|
+
case "uml":
|
|
95
|
+
return deserializeUML(input);
|
|
96
|
+
case "dfd":
|
|
97
|
+
return deserializeDFD(input);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function deserializeERD(input) {
|
|
101
|
+
const entities = [];
|
|
102
|
+
const relations = [];
|
|
103
|
+
const lines = input.split("\n");
|
|
104
|
+
let currentEntity = null;
|
|
105
|
+
for (const rawLine of lines) {
|
|
106
|
+
const line = rawLine.trim();
|
|
107
|
+
const entityMatch = line.match(/^\[(.+)\]$/);
|
|
108
|
+
if (entityMatch) {
|
|
109
|
+
currentEntity = {
|
|
110
|
+
id: entityMatch[1].toLowerCase().replace(/\s+/g, "_"),
|
|
111
|
+
name: entityMatch[1],
|
|
112
|
+
fields: []
|
|
113
|
+
};
|
|
114
|
+
entities.push(currentEntity);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (currentEntity && rawLine.startsWith(" ") && line.length > 0) {
|
|
118
|
+
const parts = line.split(/\s+/);
|
|
119
|
+
const field = { name: parts[0] };
|
|
120
|
+
if (parts.length > 1 && !["PK", "FK", "?"].includes(parts[1])) {
|
|
121
|
+
field.type = parts[1];
|
|
122
|
+
}
|
|
123
|
+
if (parts.includes("PK")) field.primary = true;
|
|
124
|
+
if (parts.includes("FK")) field.foreign = true;
|
|
125
|
+
if (parts.includes("?")) field.nullable = true;
|
|
126
|
+
currentEntity.fields.push(field);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const relMatch = line.match(
|
|
130
|
+
/^(\S+)\s+(1--1|1--\*|\*--\*)\s+(\S+)(?:\s*:\s*(.+))?$/
|
|
131
|
+
);
|
|
132
|
+
if (relMatch) {
|
|
133
|
+
currentEntity = null;
|
|
134
|
+
const fromName = relMatch[1];
|
|
135
|
+
const marker = relMatch[2];
|
|
136
|
+
const toName = relMatch[3];
|
|
137
|
+
const label = relMatch[4];
|
|
138
|
+
const fromEntity = entities.find((e) => e.name === fromName);
|
|
139
|
+
const toEntity = entities.find((e) => e.name === toName);
|
|
140
|
+
if (fromEntity && toEntity) {
|
|
141
|
+
relations.push({
|
|
142
|
+
id: `${fromEntity.id}_${toEntity.id}`,
|
|
143
|
+
from: fromEntity.id,
|
|
144
|
+
to: toEntity.id,
|
|
145
|
+
type: parseERDMarker(marker),
|
|
146
|
+
label
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (line === "") {
|
|
152
|
+
currentEntity = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { entities, relations };
|
|
156
|
+
}
|
|
157
|
+
function parseERDMarker(marker) {
|
|
158
|
+
switch (marker) {
|
|
159
|
+
case "1--1":
|
|
160
|
+
return "one-to-one";
|
|
161
|
+
case "1--*":
|
|
162
|
+
return "one-to-many";
|
|
163
|
+
case "*--*":
|
|
164
|
+
return "many-to-many";
|
|
165
|
+
default:
|
|
166
|
+
return "one-to-many";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function deserializeUML(input) {
|
|
170
|
+
const entities = [];
|
|
171
|
+
const relations = [];
|
|
172
|
+
const lines = input.split("\n");
|
|
173
|
+
let currentEntity = null;
|
|
174
|
+
for (const rawLine of lines) {
|
|
175
|
+
const line = rawLine.trim();
|
|
176
|
+
if (line === "@startuml" || line === "@enduml" || line === "") continue;
|
|
177
|
+
const classMatch = line.match(/^class\s+(\S+)\s*\{$/);
|
|
178
|
+
if (classMatch) {
|
|
179
|
+
currentEntity = {
|
|
180
|
+
id: classMatch[1].toLowerCase().replace(/\s+/g, "_"),
|
|
181
|
+
name: classMatch[1],
|
|
182
|
+
fields: []
|
|
183
|
+
};
|
|
184
|
+
entities.push(currentEntity);
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (line === "}") {
|
|
188
|
+
currentEntity = null;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (currentEntity) {
|
|
192
|
+
const fieldMatch = line.match(
|
|
193
|
+
/^(\S+)\s*:\s*(\S+?)(\?)?(?:\s*<<(PK|FK)>>)?$/
|
|
194
|
+
);
|
|
195
|
+
if (fieldMatch) {
|
|
196
|
+
const field = {
|
|
197
|
+
name: fieldMatch[1],
|
|
198
|
+
type: fieldMatch[2]
|
|
199
|
+
};
|
|
200
|
+
if (fieldMatch[3]) field.nullable = true;
|
|
201
|
+
if (fieldMatch[4] === "PK") field.primary = true;
|
|
202
|
+
if (fieldMatch[4] === "FK") field.foreign = true;
|
|
203
|
+
currentEntity.fields.push(field);
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const relMatch = line.match(
|
|
208
|
+
/^(\S+)\s+"([1*])"\s+--\s+"([1*])"\s+(\S+)(?:\s*:\s*(.+))?$/
|
|
209
|
+
);
|
|
210
|
+
if (relMatch) {
|
|
211
|
+
const fromName = relMatch[1];
|
|
212
|
+
const fromCard = relMatch[2];
|
|
213
|
+
const toCard = relMatch[3];
|
|
214
|
+
const toName = relMatch[4];
|
|
215
|
+
const label = relMatch[5];
|
|
216
|
+
const fromEntity = entities.find((e) => e.name === fromName);
|
|
217
|
+
const toEntity = entities.find((e) => e.name === toName);
|
|
218
|
+
if (fromEntity && toEntity) {
|
|
219
|
+
const type = fromCard === "1" && toCard === "1" ? "one-to-one" : fromCard === "1" && toCard === "*" ? "one-to-many" : "many-to-many";
|
|
220
|
+
relations.push({
|
|
221
|
+
id: `${fromEntity.id}_${toEntity.id}`,
|
|
222
|
+
from: fromEntity.id,
|
|
223
|
+
to: toEntity.id,
|
|
224
|
+
type,
|
|
225
|
+
label
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return { entities, relations };
|
|
231
|
+
}
|
|
232
|
+
function deserializeDFD(input) {
|
|
233
|
+
const entities = [];
|
|
234
|
+
const relations = [];
|
|
235
|
+
const lines = input.split("\n");
|
|
236
|
+
for (const rawLine of lines) {
|
|
237
|
+
const line = rawLine.trim();
|
|
238
|
+
if (line === "") continue;
|
|
239
|
+
const entityMatch = line.match(/^entity\s+(\S+)$/);
|
|
240
|
+
if (entityMatch) {
|
|
241
|
+
entities.push({
|
|
242
|
+
id: entityMatch[1].toLowerCase().replace(/\s+/g, "_"),
|
|
243
|
+
name: entityMatch[1],
|
|
244
|
+
fields: []
|
|
245
|
+
});
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const flowMatch = line.match(
|
|
249
|
+
/^(\S+)\s+->\s+(\S+)(?:\s+"(.+)")?$/
|
|
250
|
+
);
|
|
251
|
+
if (flowMatch) {
|
|
252
|
+
const fromName = flowMatch[1];
|
|
253
|
+
const toName = flowMatch[2];
|
|
254
|
+
const label = flowMatch[3];
|
|
255
|
+
const fromEntity = entities.find((e) => e.name === fromName);
|
|
256
|
+
const toEntity = entities.find((e) => e.name === toName);
|
|
257
|
+
if (fromEntity && toEntity) {
|
|
258
|
+
relations.push({
|
|
259
|
+
id: `${fromEntity.id}_${toEntity.id}`,
|
|
260
|
+
from: fromEntity.id,
|
|
261
|
+
to: toEntity.id,
|
|
262
|
+
type: "one-to-many",
|
|
263
|
+
label
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return { entities, relations };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export { deserializeSchema, serializeToDFD, serializeToERD, serializeToUML };
|
|
272
|
+
//# sourceMappingURL=diagram.serializers-OK4HP7AB.js.map
|
|
273
|
+
//# sourceMappingURL=diagram.serializers-OK4HP7AB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/Diagram/diagram.serializers.ts"],"names":[],"mappings":"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","file":"diagram.serializers-OK4HP7AB.js","sourcesContent":["import type {\n DiagramSchema,\n DiagramEntityData,\n DiagramRelationData,\n DiagramFieldData,\n ExportFormat,\n RelationType,\n} from \"./Diagram.types\";\n\n/**\n * Serialize a diagram schema to PlantUML-style ERD text.\n *\n * Example output:\n * ```\n * [Users]\n * id int PK\n * email varchar\n *\n * [Posts]\n * id int PK\n * user_id int FK\n *\n * Users 1--* Posts\n * ```\n */\nexport function serializeToERD(schema: DiagramSchema): string {\n const lines: string[] = [];\n\n for (const entity of schema.entities) {\n lines.push(`[${entity.name}]`);\n if (entity.fields) {\n for (const field of entity.fields) {\n const parts = [` ${field.name}`];\n if (field.type) parts.push(field.type);\n if (field.primary) parts.push(\"PK\");\n if (field.foreign) parts.push(\"FK\");\n if (field.nullable) parts.push(\"?\");\n lines.push(parts.join(\" \"));\n }\n }\n lines.push(\"\");\n }\n\n for (const rel of schema.relations) {\n const fromEntity = schema.entities.find((e) => e.id === rel.from);\n const toEntity = schema.entities.find((e) => e.id === rel.to);\n if (!fromEntity || !toEntity) continue;\n\n const marker = getERDMarker(rel.type);\n const parts = [fromEntity.name, marker, toEntity.name];\n if (rel.label) parts.push(`: ${rel.label}`);\n lines.push(parts.join(\" \"));\n }\n\n return lines.join(\"\\n\").trim();\n}\n\nfunction getERDMarker(type: RelationType): string {\n switch (type) {\n case \"one-to-one\":\n return \"1--1\";\n case \"one-to-many\":\n return \"1--*\";\n case \"many-to-many\":\n return \"*--*\";\n }\n}\n\n/**\n * Serialize a diagram schema to PlantUML class diagram text.\n */\nexport function serializeToUML(schema: DiagramSchema): string {\n const lines: string[] = [\"@startuml\"];\n\n for (const entity of schema.entities) {\n lines.push(`class ${entity.name} {`);\n if (entity.fields) {\n for (const field of entity.fields) {\n const typeStr = field.type ?? \"any\";\n const nullable = field.nullable ? \"?\" : \"\";\n const stereotype = field.primary\n ? \" <<PK>>\"\n : field.foreign\n ? \" <<FK>>\"\n : \"\";\n lines.push(` ${field.name} : ${typeStr}${nullable}${stereotype}`);\n }\n }\n lines.push(\"}\");\n lines.push(\"\");\n }\n\n for (const rel of schema.relations) {\n const fromEntity = schema.entities.find((e) => e.id === rel.from);\n const toEntity = schema.entities.find((e) => e.id === rel.to);\n if (!fromEntity || !toEntity) continue;\n\n const arrow = getUMLArrow(rel.type);\n const label = rel.label ? ` : ${rel.label}` : \"\";\n lines.push(`${fromEntity.name} ${arrow} ${toEntity.name}${label}`);\n }\n\n lines.push(\"@enduml\");\n return lines.join(\"\\n\");\n}\n\nfunction getUMLArrow(type: RelationType): string {\n switch (type) {\n case \"one-to-one\":\n return '\"1\" -- \"1\"';\n case \"one-to-many\":\n return '\"1\" -- \"*\"';\n case \"many-to-many\":\n return '\"*\" -- \"*\"';\n }\n}\n\n/**\n * Serialize a diagram schema to a simple DFD text format.\n */\nexport function serializeToDFD(schema: DiagramSchema): string {\n const lines: string[] = [];\n\n for (const entity of schema.entities) {\n lines.push(`entity ${entity.name}`);\n }\n\n lines.push(\"\");\n\n for (const rel of schema.relations) {\n const fromEntity = schema.entities.find((e) => e.id === rel.from);\n const toEntity = schema.entities.find((e) => e.id === rel.to);\n if (!fromEntity || !toEntity) continue;\n\n const label = rel.label ? ` \"${rel.label}\"` : \"\";\n lines.push(`${fromEntity.name} -> ${toEntity.name}${label}`);\n }\n\n return lines.join(\"\\n\").trim();\n}\n\n/**\n * Parse a serialized schema string back into a DiagramSchema.\n */\nexport function deserializeSchema(\n input: string,\n format: ExportFormat,\n): DiagramSchema {\n switch (format) {\n case \"erd\":\n return deserializeERD(input);\n case \"uml\":\n return deserializeUML(input);\n case \"dfd\":\n return deserializeDFD(input);\n }\n}\n\nfunction deserializeERD(input: string): DiagramSchema {\n const entities: DiagramEntityData[] = [];\n const relations: DiagramRelationData[] = [];\n const lines = input.split(\"\\n\");\n\n let currentEntity: DiagramEntityData | null = null;\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n\n // Entity header: [EntityName]\n const entityMatch = line.match(/^\\[(.+)\\]$/);\n if (entityMatch) {\n currentEntity = {\n id: entityMatch[1].toLowerCase().replace(/\\s+/g, \"_\"),\n name: entityMatch[1],\n fields: [],\n };\n entities.push(currentEntity);\n continue;\n }\n\n // Field line (indented): name type PK FK ?\n if (currentEntity && rawLine.startsWith(\" \") && line.length > 0) {\n const parts = line.split(/\\s+/);\n const field: DiagramFieldData = { name: parts[0] };\n if (parts.length > 1 && ![\"PK\", \"FK\", \"?\"].includes(parts[1])) {\n field.type = parts[1];\n }\n if (parts.includes(\"PK\")) field.primary = true;\n if (parts.includes(\"FK\")) field.foreign = true;\n if (parts.includes(\"?\")) field.nullable = true;\n currentEntity.fields!.push(field);\n continue;\n }\n\n // Relation: EntityA 1--* EntityB : label\n const relMatch = line.match(\n /^(\\S+)\\s+(1--1|1--\\*|\\*--\\*)\\s+(\\S+)(?:\\s*:\\s*(.+))?$/,\n );\n if (relMatch) {\n currentEntity = null;\n const fromName = relMatch[1];\n const marker = relMatch[2];\n const toName = relMatch[3];\n const label = relMatch[4];\n\n const fromEntity = entities.find((e) => e.name === fromName);\n const toEntity = entities.find((e) => e.name === toName);\n if (fromEntity && toEntity) {\n relations.push({\n id: `${fromEntity.id}_${toEntity.id}`,\n from: fromEntity.id,\n to: toEntity.id,\n type: parseERDMarker(marker),\n label,\n });\n }\n continue;\n }\n\n // Empty line resets current entity context\n if (line === \"\") {\n currentEntity = null;\n }\n }\n\n return { entities, relations };\n}\n\nfunction parseERDMarker(marker: string): RelationType {\n switch (marker) {\n case \"1--1\":\n return \"one-to-one\";\n case \"1--*\":\n return \"one-to-many\";\n case \"*--*\":\n return \"many-to-many\";\n default:\n return \"one-to-many\";\n }\n}\n\nfunction deserializeUML(input: string): DiagramSchema {\n const entities: DiagramEntityData[] = [];\n const relations: DiagramRelationData[] = [];\n const lines = input.split(\"\\n\");\n\n let currentEntity: DiagramEntityData | null = null;\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n\n if (line === \"@startuml\" || line === \"@enduml\" || line === \"\") continue;\n\n // Class header: class EntityName {\n const classMatch = line.match(/^class\\s+(\\S+)\\s*\\{$/);\n if (classMatch) {\n currentEntity = {\n id: classMatch[1].toLowerCase().replace(/\\s+/g, \"_\"),\n name: classMatch[1],\n fields: [],\n };\n entities.push(currentEntity);\n continue;\n }\n\n // Closing brace\n if (line === \"}\") {\n currentEntity = null;\n continue;\n }\n\n // Field: name : type<<stereotype>>\n if (currentEntity) {\n const fieldMatch = line.match(\n /^(\\S+)\\s*:\\s*(\\S+?)(\\?)?(?:\\s*<<(PK|FK)>>)?$/,\n );\n if (fieldMatch) {\n const field: DiagramFieldData = {\n name: fieldMatch[1],\n type: fieldMatch[2],\n };\n if (fieldMatch[3]) field.nullable = true;\n if (fieldMatch[4] === \"PK\") field.primary = true;\n if (fieldMatch[4] === \"FK\") field.foreign = true;\n currentEntity.fields!.push(field);\n }\n continue;\n }\n\n // Relation: EntityA \"1\" -- \"*\" EntityB : label\n const relMatch = line.match(\n /^(\\S+)\\s+\"([1*])\"\\s+--\\s+\"([1*])\"\\s+(\\S+)(?:\\s*:\\s*(.+))?$/,\n );\n if (relMatch) {\n const fromName = relMatch[1];\n const fromCard = relMatch[2];\n const toCard = relMatch[3];\n const toName = relMatch[4];\n const label = relMatch[5];\n\n const fromEntity = entities.find((e) => e.name === fromName);\n const toEntity = entities.find((e) => e.name === toName);\n if (fromEntity && toEntity) {\n const type: RelationType =\n fromCard === \"1\" && toCard === \"1\"\n ? \"one-to-one\"\n : fromCard === \"1\" && toCard === \"*\"\n ? \"one-to-many\"\n : \"many-to-many\";\n relations.push({\n id: `${fromEntity.id}_${toEntity.id}`,\n from: fromEntity.id,\n to: toEntity.id,\n type,\n label,\n });\n }\n }\n }\n\n return { entities, relations };\n}\n\nfunction deserializeDFD(input: string): DiagramSchema {\n const entities: DiagramEntityData[] = [];\n const relations: DiagramRelationData[] = [];\n const lines = input.split(\"\\n\");\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (line === \"\") continue;\n\n // Entity: entity EntityName\n const entityMatch = line.match(/^entity\\s+(\\S+)$/);\n if (entityMatch) {\n entities.push({\n id: entityMatch[1].toLowerCase().replace(/\\s+/g, \"_\"),\n name: entityMatch[1],\n fields: [],\n });\n continue;\n }\n\n // Flow: EntityA -> EntityB \"label\"\n const flowMatch = line.match(\n /^(\\S+)\\s+->\\s+(\\S+)(?:\\s+\"(.+)\")?$/,\n );\n if (flowMatch) {\n const fromName = flowMatch[1];\n const toName = flowMatch[2];\n const label = flowMatch[3];\n\n const fromEntity = entities.find((e) => e.name === fromName);\n const toEntity = entities.find((e) => e.name === toName);\n if (fromEntity && toEntity) {\n relations.push({\n id: `${fromEntity.id}_${toEntity.id}`,\n from: fromEntity.id,\n to: toEntity.id,\n type: \"one-to-many\",\n label,\n });\n }\n }\n }\n\n return { entities, relations };\n}\n"]}
|