From b90adc23d409b091e04b6d6a6b8afff769c77749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Tue, 5 Nov 2024 12:30:55 +0100 Subject: [PATCH] feat(react-docs): add reusable ui components based on shadcn/ui (#531) * feat(components): initialize components page * feat(components): create example component page * feat(components): setup ui components section in the docs * fix(build): add global env to turbo config * feat(components): add syntax highlighting * display other ui component dependencies correctly * removed syntax highlighter, use nextra mdx highlighter * Improve texts * Improve introduction * migrated ui project to web repo * fix typo * fix turbo.json * try to load files from file system instead of url * add test to see if build succeeds * add ui-components as dependency * update lockfile * add test to see if build succeeds * remove test * add discussions template * remove discussion template * remove some parts * chore(build): update turbo * refactor(components): cleanup zoom slider component * change ui-component request url * feat(components): add noindex,nofollow and overview page * removed from navbar * modified navbar, changed url from ui to components * changed wording from ui to component * adjusted styling of logos * fix(components): typos * fixed styling of pro site * renamed previous components to built-in components * reorganized sections * feat(components): add tracking * change * changed build outputs * chore(build): use separate task for generating the components registry * fix(build): create a separate task for generating the registry * feat(components): configure tracking * added lucide as dependency * adjust navbar for svelteflow.dev * style(navbar): re-add shortcuts to docsearch * fix merge issues with main * delete unused whats new script * moved configs out of packages folder * removed xy-stylguide app * reinstalled * updated dependencies * reinstalled dependencies * updated next dependencies and moved mdx-compile code to xy-shared * cleaned up global styles * fixed up xyflow.com * moved fonts to xy-shared * moved references out of page-data * get rid of styles folder * moved all of the getmdxunderroute functions to xy-shared * implemented useConfigContext and remove -with-frontmatter wrappers * moved sites to layouts, got rid of a couple of components * fix imports * remove last -with-frontmatter components and move contents of sites to layouts * updated to a single typescript version * updated to a single eslint version * updated to a single react version * updated all dependencies of xy-ui * chore(components): minor text adjustments * fix(zoom-slider): add button as a dep * updated all dependencies and removed all unsused dependencies * updated some more dependencies * fixed lint error * reworked the way routes are handled for the RemoteCodeViewer * added static code viewer * cleaned up some more code and added scrollable tablist * use npm2yarn for our codeblock * fix(react-docs): remove duplicate pro label from example pages * fix style * use new code viewer everywhere * fix huge import sizes * activate turbo tui * fix showcase border * update turbo and pnpm * updated dependencies one last time :) * fix title rendering & remove head component * remove falsey export * upgrade npm2yarn, now npx commands are also transformed * feat(ui-components): add PlaceholderNode component * feat(ui-components): add new placeholder node page and link * fix big bundle size * feat(ui-components): create new annotation component * refactor(ui-components): improvements to export function placeholder node * fix(placeholderNode): typo * tried to make it look nices * fix(ui-components): missing display name * :memo: Create a blog post announcing our new components release. * :memo: Create a whats-new entry for the components release on reactflow.dev . * updated turbo.json to reflect build dependencies * removed scrollbar styling for code viewer * fix(build): change import * refactor(blog): fix some typos * update turbo.json * feat(ui-components): create labeled group node * re-added code viewer options, and refined styles for horizontal and vertical layouts * updated turbo.json * updated turbo.json * docs: add abbey to team description :) * add open in stackblitz button to new code viewer * feat(ui-components): create zoom select component * style(ui-components): fix placeholder node appearance * add open in codesanbox button * feat(ui-components): create devtools component * remove warning from tailwind example, it works now * remove old code viewer, clean up some code * fetch latest svelte flow version * register env variable * added npm2yarn to components * removed inter & favicon * typo * fixed node inspector * changed code viewer for devtools * style(ui-components): darkmode support labeled group node * refactor(ui-components): rename annotation node and add darkmode support * style(ui-components): fix placeholder node pixel offset * restructured menu * feat(ui-components): connect annotations to parent nodes * refactor(components): group components in the sidebar * make button-edge a little more general * refactor(ui-components): export all loggers; pass as props to DevTools; NodeInspector func. fixes; export all loggers -> pass as props of DevTools Component darkmode styling * fix(ui-components): remove extra import * refactor(navbar): hide elements of the navbar on smaller screens * style(ui-components): devtools button color adjustment * add floating edges to annotation example * :wrench: Change imports to use 'with' syntax for compat with recent nodejs versions. * :wrench: Include ui components demo site in xyflow.com env. * :memo: Add images and embeds to components blog post. * little bit better annotation nodes * remove console output * refactor(components): add beta callout, remove motivation in favor of blog * refactor(ui-components): add togglegroup devtools; separate tools; update docs * style(ui-components): devtools darkmode fix * perf(ui-components): add nodeClickDistance to placeholder node * Create `AnimatedSvgEdge` component. (#549) * :wrench: Change imports to use 'with' syntax for compat with recent nodejs versions. * :wrench: Include ui components demo site in xyflow.com env. * :memo: Add images and embeds to components blog post. * :sparkles: Create an AnimatedSvgEdge component. * :recycle: Use a package icon from lucide in demo. * :memo: Add page for animated svg edge component. * roll back changes to annotation node * refactor(components): put components into subfolders (#559) * refactor(tooltip-node): add handles and position option * refactor(components): fix typo * fix(components): use latest version of shadcn cli for init and installation * refactor(components): use component id in the url too * fix(ui-components): move zoom select calculation to inside selector * style(components): add some spacing * change date on whats new article * simplified labeled handle * change shadcn.config.js to components.json * refactor(components): remove beta callout from components page * fix(annotation-node): added flexibility for arrowStyles with CSSProperties * :memo: Address blog post feedback. --------- Co-authored-by: peterkogo Co-authored-by: moklick Co-authored-by: Peter Kogo <7165378+peterkogo@users.noreply.github.com> Co-authored-by: Abbey Yacoe Co-authored-by: Hayleigh Thompson --- apps/example-apps/package.json | 44 +- .../react/learn/devtools/NodeInspector.tsx | 44 +- .../react/learn/devtools/ViewportLogger.tsx | 2 +- apps/ui-components/.eslintignore | 1 + apps/ui-components/.eslintrc.json | 3 + apps/ui-components/.gitignore | 39 + apps/ui-components/.prettierrc | 3 + apps/ui-components/README.md | 37 + .../app/components/animated-svg-edge/page.tsx | 10 + .../app/components/annotation-node/page.tsx | 10 + .../app/components/base-handle/page.tsx | 30 + .../app/components/base-node/page.tsx | 10 + .../app/components/button-edge/page.tsx | 10 + .../components/database-schema-node/page.tsx | 10 + .../app/components/devtools/page.tsx | 10 + .../components/labeled-group-node/page.tsx | 10 + .../app/components/labeled-handle/page.tsx | 30 + .../app/components/placeholder-node/page.tsx | 10 + .../app/components/tooltip-node/page.tsx | 10 + .../app/components/zoom-select/page.tsx | 10 + .../app/components/zoom-slider/page.tsx | 10 + apps/ui-components/app/globals.css | 79 + apps/ui-components/app/layout.tsx | 26 + apps/ui-components/app/page.tsx | 42 + apps/ui-components/components.json | 21 + .../ui-components/components/demo-wrapper.tsx | 12 + .../components/theme-switcher.tsx | 36 + apps/ui-components/components/ui/button.tsx | 57 + apps/ui-components/components/ui/select.tsx | 164 + apps/ui-components/components/ui/slider.tsx | 28 + apps/ui-components/components/ui/table.tsx | 120 + .../components/ui/toggle-group.tsx | 61 + apps/ui-components/components/ui/toggle.tsx | 45 + apps/ui-components/lib/utils.ts | 6 + apps/ui-components/middleware.ts | 16 + apps/ui-components/next.config.js | 13 + apps/ui-components/package.json | 46 + apps/ui-components/postcss.config.mjs | 8 + .../components/animated-svg-edge/demo.tsx | 51 + .../components/animated-svg-edge/index.tsx | 247 + .../animated-svg-edge/registry.json | 16 + .../components/annotation-node/demo.tsx | 79 + .../components/annotation-node/index.tsx | 32 + .../components/annotation-node/registry.json | 16 + .../registry/components/base-handle/demo.tsx | 20 + .../registry/components/base-handle/index.tsx | 11 + .../components/base-handle/registry.json | 16 + .../registry/components/base-node/demo.tsx | 47 + .../registry/components/base-node/index.tsx | 19 + .../components/base-node/registry.json | 16 + .../registry/components/button-edge/demo.tsx | 45 + .../registry/components/button-edge/index.tsx | 53 + .../components/button-edge/registry.json | 16 + .../components/database-schema-node/demo.tsx | 88 + .../components/database-schema-node/index.tsx | 52 + .../database-schema-node/registry.json | 20 + .../registry/components/devtools/demo.tsx | 40 + .../registry/components/devtools/index.tsx | 244 + .../components/devtools/registry.json | 16 + .../components/labeled-group-node/demo.tsx | 46 + .../components/labeled-group-node/index.tsx | 23 + .../labeled-group-node/registry.json | 16 + .../components/labeled-handle/demo.tsx | 57 + .../components/labeled-handle/index.tsx | 45 + .../components/labeled-handle/registry.json | 16 + .../components/placeholder-node/demo.tsx | 53 + .../components/placeholder-node/index.tsx | 59 + .../components/placeholder-node/registry.json | 16 + .../registry/components/tooltip-node/demo.tsx | 70 + .../components/tooltip-node/index.tsx | 41 + .../components/tooltip-node/registry.json | 16 + .../registry/components/zoom-select/demo.tsx | 21 + .../registry/components/zoom-select/index.tsx | 76 + .../components/zoom-select/registry.json | 16 + .../registry/components/zoom-slider/demo.tsx | 21 + .../registry/components/zoom-slider/index.tsx | 80 + .../components/zoom-slider/registry.json | 16 + .../scripts/generate-registry.js | 84 + .../scripts/init-new-component.js | 67 + apps/ui-components/tailwind.config.ts | 96 + .../templates/component/demo.tsx | 21 + .../templates/component/index.tsx | 3 + .../templates/component/registry.json | 16 + apps/ui-components/templates/page/page.tsx | 10 + apps/ui-components/tsconfig.json | 26 + apps/xy-styleguide/.eslintrc.cjs | 16 - apps/xy-styleguide/.gitignore | 24 - apps/xy-styleguide/.storybook/main.ts | 26 - .../.storybook/preview-head.html | 31 - apps/xy-styleguide/.storybook/preview.ts | 17 - apps/xy-styleguide/index.html | 13 - apps/xy-styleguide/package.json | 47 - apps/xy-styleguide/postcss.config.js | 7 - apps/xy-styleguide/public/vite.svg | 1 - apps/xy-styleguide/src/App.css | 42 - apps/xy-styleguide/src/App.tsx | 35 - apps/xy-styleguide/src/assets/react.svg | 1 - apps/xy-styleguide/src/index.css | 69 - apps/xy-styleguide/src/main.tsx | 10 - .../src/stories/Accordion.stories.tsx | 63 - .../src/stories/Button.stories.tsx | 64 - .../src/stories/Checkbox.stories.tsx | 21 - .../src/stories/Container.stories.tsx | 31 - .../src/stories/Introduction.mdx | 213 - .../src/stories/ListWrapper.stories.tsx | 42 - .../src/stories/PricingTable.stories.tsx | 17 - .../src/stories/RadioGroup.stories.tsx | 36 - .../src/stories/Select.stories.tsx | 51 - .../src/stories/assets/code-brackets.svg | 1 - .../src/stories/assets/colors.svg | 1 - .../src/stories/assets/comments.svg | 1 - .../src/stories/assets/direction.svg | 1 - .../xy-styleguide/src/stories/assets/flow.svg | 1 - .../src/stories/assets/plugin.svg | 1 - .../xy-styleguide/src/stories/assets/repo.svg | 1 - .../src/stories/assets/stackalt.svg | 1 - apps/xy-styleguide/src/styles/tailwind.css | 56 - apps/xy-styleguide/src/vite-env.d.ts | 1 - apps/xy-styleguide/tailwind.config.js | 16 - apps/xy-styleguide/tsconfig.json | 25 - apps/xy-styleguide/tsconfig.node.json | 10 - apps/xy-styleguide/vite.config.ts | 8 - .../eslint-config-xyflow/index.js | 0 configs/eslint-config-xyflow/package.json | 16 + .../xy-tailwind-config/index.js | 0 .../xy-tailwind-config/package.json | 2 +- {packages => configs}/xy-tsconfig/base.json | 0 {packages => configs}/xy-tsconfig/nextjs.json | 0 .../xy-tsconfig/package.json | 0 .../xy-tsconfig/react-library.json | 0 package.json | 23 +- packages/eslint-config-xyflow/package.json | 16 - packages/xy-shared/components/head.tsx | 49 - .../xy-shared/components/sidebar-title.tsx | 2 +- packages/xy-shared/context/shared-context.tsx | 11 + .../xy-shared}/fonts/NTDapper-black.woff2 | Bin .../xy-shared}/fonts/NTDapper-bold.woff2 | Bin .../xy-shared}/fonts/NTDapper-medium.woff2 | Bin .../xy-shared}/fonts/NTDapper-regular.woff2 | Bin packages/xy-shared/fonts/index.ts | 20 + packages/xy-shared/index.tsx | 9 +- packages/xy-shared/layouts/blog-post-base.tsx | 91 + packages/xy-shared/layouts/blog-post.tsx | 101 +- packages/xy-shared/layouts/case-study.tsx | 25 +- packages/xy-shared/layouts/example.tsx | 18 +- packages/xy-shared/layouts/tutorial.tsx | 22 + .../xy-shared/lib/{utils.tsx => index.tsx} | 44 +- packages/xy-shared/package.json | 63 +- .../xy-shared/server/compile-code-snippet.ts | 47 + packages/xy-shared/server/get-static-code.ts | 46 + packages/xy-shared/server/index.ts | 4 + .../server}/mdx-content-under-route.ts | 14 +- packages/xy-shared/server/utils.ts | 10 + .../xy-shared/{lib => styles}/globals.css | 83 +- packages/xy-shared/types/index.tsx | 11 + .../xy-shared/widgets/about-section/index.tsx | 2 +- .../xy-shared/widgets/hero-flow/fiber.tsx | 3 +- .../image-slider/image-slider-item.tsx | 2 +- .../image-slider/image-slider-items.tsx | 2 +- .../widgets/remote-code-viewer/fetchFiles.ts | 45 + .../widgets/remote-code-viewer/index.tsx | 274 +- .../open-in-codesandbox.tsx | 106 +- .../remote-code-viewer/open-in-stackblitz.tsx | 37 +- .../widgets/remote-code-viewer/style.css | 4 + packages/xy-shared/widgets/search/index.tsx | 14 +- packages/xy-ui/components/ui/logo.tsx | 9 +- packages/xy-ui/components/ui/tabs.tsx | 19 +- packages/xy-ui/package.json | 44 +- pnpm-lock.yaml | 23277 +++++----------- pnpm-workspace.yaml | 1 + scripts/create-whats-new/index.js | 25 - scripts/create-whats-new/package.json | 20 - scripts/create-whats-new/whats-new-creator.js | 80 - sites/reactflow.dev/.env | 6 +- sites/reactflow.dev/.env.development | 1 + sites/reactflow.dev/next.config.mjs | 13 +- sites/reactflow.dev/package.json | 59 +- sites/reactflow.dev/redirects.json | 5 + .../src/components/ui-component-viewer.tsx | 157 + sites/reactflow.dev/src/global.css | 46 + .../src/layouts/case-study-with-metadata.tsx | 53 - .../src/layouts/example-with-frontmatter.tsx | 29 - .../{sites => layouts}/examples/overview.tsx | 3 +- .../{sites => layouts}/home/flows/flow-a.tsx | 0 .../{sites => layouts}/home/flows/flow-b.tsx | 0 .../{sites => layouts}/home/flows/flow-c.tsx | 0 .../src/{sites => layouts}/home/index.tsx | 2 +- .../{sites => layouts}/pro/case-studies.tsx | 10 +- .../src/{sites => layouts}/pro/enterprise.tsx | 0 .../src/{sites => layouts}/pro/examples.tsx | 9 +- .../src/{sites => layouts}/pro/index.tsx | 2 +- .../src/{sites => layouts}/pro/pricing.tsx | 0 .../{sites => layouts}/pro/quote-request.tsx | 0 .../src/{sites => layouts}/showcase.tsx | 0 .../{sites => layouts}/surveys/flow/edges.tsx | 0 .../{sites => layouts}/surveys/flow/hooks.ts | 0 .../{sites => layouts}/surveys/flow/nodes.tsx | 0 .../surveys/reactflow-2023.tsx | 0 .../src/layouts/tutorial-with-frontmatter.tsx | 49 - .../src/{sites => layouts}/tutorials.tsx | 10 +- .../src/{sites => layouts}/whats-new.tsx | 0 sites/reactflow.dev/src/pages/_app.tsx | 33 +- sites/reactflow.dev/src/pages/_meta.ts | 51 +- .../api-reference/components/background.mdx | 2 +- .../api-reference/components/base-edge.mdx | 2 +- .../api-reference/components/controls.mdx | 2 +- .../components/edge-label-renderer.mdx | 2 +- .../api-reference/components/edge-text.mdx | 2 +- .../pages/api-reference/components/handle.mdx | 2 +- .../api-reference/components/minimap.mdx | 2 +- .../components/node-resize-control.mdx | 2 +- .../api-reference/components/node-resizer.mdx | 10 +- .../api-reference/components/node-toolbar.mdx | 2 +- .../pages/api-reference/components/panel.mdx | 2 +- .../api-reference/hooks/use-connection.mdx | 2 +- .../api-reference/hooks/use-edges-state.mdx | 2 +- .../pages/api-reference/hooks/use-edges.mdx | 2 +- .../hooks/use-handle-connections.mdx | 2 +- .../api-reference/hooks/use-internal-node.mdx | 2 +- .../api-reference/hooks/use-key-press.mdx | 2 +- .../pages/api-reference/hooks/use-node-id.mdx | 2 +- .../api-reference/hooks/use-nodes-data.mdx | 2 +- .../hooks/use-nodes-initialized.mdx | 2 +- .../api-reference/hooks/use-nodes-state.mdx | 2 +- .../pages/api-reference/hooks/use-nodes.mdx | 2 +- .../hooks/use-on-selection-change.mdx | 2 +- .../hooks/use-on-viewport-change.mdx | 2 +- .../api-reference/hooks/use-react-flow.mdx | 2 +- .../api-reference/hooks/use-store-api.mdx | 2 +- .../pages/api-reference/hooks/use-store.mdx | 2 +- .../hooks/use-update-node-internals.mdx | 2 +- .../api-reference/hooks/use-viewport.mdx | 2 +- .../src/pages/api-reference/react-flow.mdx | 4 +- .../types/connection-line-component-props.mdx | 2 +- .../api-reference/types/connection-state.mdx | 2 +- .../pages/api-reference/types/connection.mdx | 2 +- .../types/default-edge-options.mdx | 2 +- .../pages/api-reference/types/edge-change.mdx | 2 +- .../pages/api-reference/types/edge-marker.mdx | 2 +- .../pages/api-reference/types/edge-props.mdx | 2 +- .../src/pages/api-reference/types/edge.mdx | 2 +- .../api-reference/types/fit-view-options.mdx | 2 +- .../api-reference/types/handle-connection.mdx | 2 +- .../src/pages/api-reference/types/handle.mdx | 2 +- .../api-reference/types/internal-node.mdx | 27 +- .../types/mini-map-node-props.mdx | 2 +- .../pages/api-reference/types/node-change.mdx | 2 +- .../pages/api-reference/types/node-props.mdx | 2 +- .../src/pages/api-reference/types/node.mdx | 2 +- .../api-reference/types/on-edges-change.mdx | 2 +- .../api-reference/types/on-nodes-change.mdx | 2 +- .../types/react-flow-instance.mdx | 2 +- .../types/react-flow-json-object.mdx | 2 +- .../api-reference/types/resize-params.mdx | 2 +- .../pages/api-reference/types/viewport.mdx | 2 +- .../pages/api-reference/utils/add-edge.mdx | 2 +- .../utils/apply-edge-changes.mdx | 2 +- .../utils/apply-node-changes.mdx | 2 +- .../api-reference/utils/get-bezier-path.mdx | 2 +- .../utils/get-connected-edges.mdx | 4 +- .../api-reference/utils/get-incomers.mdx | 2 +- .../api-reference/utils/get-nodes-bounds.mdx | 2 +- .../api-reference/utils/get-outgoers.mdx | 2 +- .../utils/get-simple-bezier-path.mdx | 2 +- .../utils/get-smooth-step-path.mdx | 2 +- .../api-reference/utils/get-straight-path.mdx | 2 +- .../utils/get-transform-for-bounds.mdx | 2 +- .../utils/get-viewport-for-bounds.mdx | 2 +- .../src/pages/api-reference/utils/is-edge.mdx | 2 +- .../src/pages/api-reference/utils/is-node.mdx | 2 +- .../api-reference/utils/reconnect-edge.mdx | 2 +- .../src/pages/components/_meta.tsx | 23 + .../src/pages/components/controls/_meta.tsx | 4 + .../pages/components/controls/zoom-select.mdx | 15 + .../pages/components/controls/zoom-slider.mdx | 15 + .../src/pages/components/edges/_meta.tsx | 4 + .../components/edges/animated-svg-edge.mdx | 61 + .../pages/components/edges/button-edge.mdx | 15 + .../src/pages/components/handles/_meta.tsx | 4 + .../pages/components/handles/base-handle.mdx | 15 + .../components/handles/labeled-handle.mdx | 15 + .../src/pages/components/index.mdx | 35 + .../src/pages/components/misc/devtools.mdx | 25 + .../src/pages/components/nodes/_meta.tsx | 8 + .../components/nodes/annotation-node.mdx | 15 + .../src/pages/components/nodes/base-node.mdx | 15 + .../components/nodes/database-schema-node.mdx | 15 + .../components/nodes/labeled-group-node.mdx | 15 + .../components/nodes/placeholder-node.mdx | 15 + .../pages/components/nodes/tooltip-node.mdx | 15 + .../src/pages/developer-survey-2023.mdx | 2 +- .../pages/examples/edges/animating-edges.mdx | 12 +- .../examples/edges/custom-connectionline.mdx | 9 +- .../src/pages/examples/edges/custom-edges.mdx | 10 +- .../examples/edges/delete-edge-on-drop.mdx | 9 +- .../examples/edges/edge-label-renderer.mdx | 9 +- .../src/pages/examples/edges/edge-types.mdx | 12 +- .../pages/examples/edges/editable-edge.mdx | 2 +- .../pages/examples/edges/floating-edges.mdx | 12 +- .../src/pages/examples/edges/markers.mdx | 12 +- .../examples/edges/multi-connection-line.mdx | 9 +- .../pages/examples/edges/reconnect-edge.mdx | 12 +- .../examples/edges/simple-floating-edges.mdx | 9 +- .../pages/examples/edges/temporary-edges.mdx | 12 +- .../src/pages/examples/index.mdx | 2 +- .../examples/interaction/collaborative.mdx | 2 +- .../examples/interaction/computing-flows.mdx | 9 +- .../interaction/connection-events.mdx | 9 +- .../examples/interaction/context-menu.mdx | 12 +- .../examples/interaction/contextual-zoom.mdx | 9 +- .../pages/examples/interaction/copy-paste.mdx | 2 +- .../examples/interaction/drag-and-drop.mdx | 9 +- .../examples/interaction/helper-lines.mdx | 2 +- .../interaction/interaction-props.mdx | 9 +- .../examples/interaction/prevent-cycles.mdx | 9 +- .../examples/interaction/save-and-restore.mdx | 9 +- .../examples/interaction/touch-device.mdx | 12 +- .../pages/examples/interaction/undo-redo.mdx | 2 +- .../pages/examples/interaction/validation.mdx | 12 +- .../examples/interaction/zoom-transitions.mdx | 9 +- .../src/pages/examples/layout/auto-layout.mdx | 2 +- .../src/pages/examples/layout/dagre.mdx | 12 +- .../layout/elkjs-multiple-handles.mdx | 9 +- .../src/pages/examples/layout/elkjs.mdx | 12 +- .../pages/examples/layout/entitree-flex.mdx | 12 +- .../pages/examples/layout/expand-collapse.mdx | 2 +- .../pages/examples/layout/force-layout.mdx | 2 +- .../src/pages/examples/layout/horizontal.mdx | 12 +- .../src/pages/examples/layout/sub-flows.mdx | 12 +- .../examples/layout/workflow-builder.mdx | 2 +- .../pages/examples/misc/download-image.mdx | 12 +- .../src/pages/examples/misc/provider.mdx | 12 +- .../misc/static-server-side-generation.mdx | 2 +- .../examples/misc/use-react-flow-hook.mdx | 12 +- .../examples/nodes/add-node-on-edge-drop.mdx | 10 +- .../pages/examples/nodes/connection-limit.mdx | 12 +- .../src/pages/examples/nodes/custom-node.mdx | 12 +- .../examples/nodes/delete-middle-node.mdx | 12 +- .../src/pages/examples/nodes/drag-handle.mdx | 12 +- .../pages/examples/nodes/dynamic-grouping.mdx | 2 +- .../src/pages/examples/nodes/easy-connect.mdx | 10 +- .../src/pages/examples/nodes/hidden.mdx | 12 +- .../pages/examples/nodes/intersections.mdx | 12 +- .../src/pages/examples/nodes/node-resizer.mdx | 12 +- .../src/pages/examples/nodes/node-toolbar.mdx | 12 +- .../examples/nodes/proximity-connect.mdx | 12 +- .../pages/examples/nodes/rotatable-node.mdx | 11 +- .../src/pages/examples/nodes/shapes.mdx | 2 +- .../src/pages/examples/nodes/stress.mdx | 10 +- .../src/pages/examples/nodes/update-node.mdx | 11 +- .../src/pages/examples/overview.mdx | 12 +- .../src/pages/examples/styling/base-style.mdx | 10 +- .../src/pages/examples/styling/dark-mode.mdx | 10 +- .../examples/styling/styled-components.mdx | 9 +- .../src/pages/examples/styling/tailwind.mdx | 23 +- .../src/pages/examples/styling/turbo-flow.mdx | 12 +- sites/reactflow.dev/src/pages/index.mdx | 24 +- .../learn/advanced-use/computing-flows.mdx | 50 +- .../advanced-use/devtools-and-debugging.mdx | 10 +- .../learn/advanced-use/state-management.mdx | 42 +- .../learn/advanced-use/uncontrolled-flow.mdx | 16 +- .../src/pages/learn/concepts/_meta.tsx | 2 +- .../learn/concepts/built-in-components.mdx | 38 + .../pages/learn/concepts/core-concepts.mdx | 22 +- .../learn/concepts/plugin-components.mdx | 50 - .../src/pages/learn/concepts/the-viewport.mdx | 16 +- .../learn/customization/custom-edges.mdx | 17 +- .../learn/customization/custom-nodes.mdx | 28 +- .../src/pages/learn/customization/theming.mdx | 11 +- .../getting-started/adding-interactivity.mdx | 17 +- .../learn/getting-started/building-a-flow.mdx | 41 +- sites/reactflow.dev/src/pages/learn/index.mdx | 104 +- .../src/pages/learn/layouting/layouting.mdx | 47 +- .../src/pages/learn/layouting/sub-flows.mdx | 26 +- .../src/pages/learn/troubleshooting/index.mdx | 16 +- .../learn/troubleshooting/migrate-to-v11.mdx | 2 +- .../learn/troubleshooting/migrate-to-v12.mdx | 22 +- .../troubleshooting/remove-attribution.mdx | 10 +- .../src/pages/learn/tutorials.mdx | 2 +- .../mind-map-app-with-react-flow.mdx | 56 +- .../react-flow-and-the-web-audio-api.mdx | 39 +- .../tutorials/slide-shows-with-react-flow.mdx | 39 +- .../pro/case-studies/carto-case-study.mdx | 2 +- .../case-studies/doubleloop-case-study.mdx | 2 +- .../pro/case-studies/hubql-case-study.mdx | 2 +- .../src/pages/pro/case-studies/index.mdx | 2 +- .../pro/case-studies/onesignal-case-study.mdx | 2 +- .../reactflow.dev/src/pages/pro/examples.mdx | 2 +- sites/reactflow.dev/src/pages/pro/index.mdx | 2 +- sites/reactflow.dev/src/pages/pro/pricing.mdx | 2 +- .../src/pages/pro/quote-request.mdx | 2 +- sites/reactflow.dev/src/pages/showcase.mdx | 2 +- sites/reactflow.dev/src/pages/whats-new.mdx | 19 +- .../src/pages/whats-new/2023-11-02.mdx | 11 +- .../src/pages/whats-new/2023-11-14.mdx | 11 +- .../src/pages/whats-new/2024-01-18.mdx | 11 +- .../src/pages/whats-new/2024-02-09.mdx | 12 +- .../src/pages/whats-new/2024-05-06.mdx | 11 +- .../src/pages/whats-new/2024-06-11.mdx | 11 +- .../src/pages/whats-new/2024-07-22.mdx | 11 +- .../src/pages/whats-new/2024-08-14.mdx | 11 +- .../src/pages/whats-new/2024-11-04.mdx | 31 + .../ReactFlow.props.ts | 0 .../components/Background.ts | 0 .../components/BaseEdge.ts | 0 .../components/Controls.ts | 0 .../components/EdgeLabelRenderer.ts | 0 .../components/EdgeText.ts | 0 .../components/Handle.ts | 0 .../components/MiniMap.ts | 0 .../components/NodeResizeControl.ts | 0 .../components/NodeResizer.ts | 0 .../components/NodeToolbar.ts | 0 .../components/Panel.ts | 0 .../hooks/useConnection.ts | 0 .../hooks/useEdges.ts | 0 .../hooks/useEdgesState.ts | 0 .../hooks/useHandleConnections.ts | 0 .../hooks/useInternalNode.ts | 0 .../hooks/useKeyPress.ts | 0 .../hooks/useNodeId.ts | 0 .../hooks/useNodes.ts | 0 .../hooks/useNodesData.ts | 0 .../hooks/useNodesInitialized.ts | 0 .../hooks/useNodesState.ts | 0 .../hooks/useOnSelectionChange.ts | 0 .../hooks/useOnViewportChange.ts | 0 .../hooks/useReactFlow.ts | 0 .../hooks/useStore.ts | 0 .../hooks/useStoreApi.ts | 0 .../hooks/useUpdateNodeInternals.ts | 0 .../hooks/useViewport.ts | 0 .../types/BackgroundVariant.ts | 0 .../types/Connection.fields.ts | 0 .../ConnectionLineComponentProps.fields.ts | 0 .../types/ConnectionState.fields.ts | 0 .../types/DefaultEdgeOptions.fields.ts | 0 .../types/Edge.fields.ts | 0 .../types/EdgeChange.fields.ts | 0 .../types/EdgeMarker.ts | 0 .../types/EdgeProps.ts | 0 .../types/FitViewOptions.ts | 0 .../types/Handle.fields.ts | 0 .../types/HandleConnection.fields.ts | 0 .../types/InternalNode.fields.ts | 0 .../types/MiniMapNodeProps.fields.ts | 0 .../types/Node.fields.ts | 0 .../types/NodeChange.fields.ts | 0 .../types/NodeProps.fields.ts | 0 .../types/OnEdgesChange.ts | 0 .../types/OnNodesChange.ts | 0 .../types/ReactFlowInstance.fields.ts | 0 .../types/ReactFlowJsonObject.fields.ts | 0 .../types/ResizeParams.ts | 0 .../types/Viewport.fields.ts | 0 .../reference => references}/utils/addEdge.ts | 0 .../utils/applyEdgeChanges.ts | 0 .../utils/applyNodeChanges.ts | 0 .../utils/getBezierPath.ts | 0 .../utils/getConnectedEdges.ts | 0 .../utils/getIncomers.ts | 0 .../utils/getMarkerEnd.ts | 0 .../utils/getOutgoers.ts | 0 .../utils/getRectOfNodes.ts | 0 .../utils/getSimpleBezierPath.ts | 0 .../utils/getSmoothStepPath.ts | 0 .../utils/getStraightPath.ts | 0 .../utils/getTransformForBounds.ts | 0 .../utils/getViewportForBounds.ts | 0 .../reference => references}/utils/isEdge.ts | 0 .../reference => references}/utils/isNode.ts | 0 .../utils/reconnectEdge.ts | 0 .../src/styles/fonts/NTDapper-black.woff2 | Bin 25336 -> 0 bytes .../src/styles/fonts/NTDapper-bold.woff2 | Bin 25652 -> 0 bytes .../src/styles/fonts/NTDapper-medium.woff2 | Bin 25584 -> 0 bytes .../src/styles/fonts/NTDapper-regular.woff2 | Bin 24676 -> 0 bytes sites/reactflow.dev/src/styles/global.css | 83 - .../api-reference-by-category.ts | 2 +- .../fetch-shadcn-component.ts | 85 + .../get-static-props/github-npm-stats.ts | 8 +- .../get-static-props/pro-examples-list.ts | 2 +- sites/reactflow.dev/src/utils/index.ts | 39 +- sites/reactflow.dev/src/utils/routes.ts | 41 +- sites/reactflow.dev/theme.config.tsx | 135 +- sites/svelteflow.dev/.env | 5 +- sites/svelteflow.dev/next-env.d.ts | 2 +- sites/svelteflow.dev/next.config.mjs | 5 +- sites/svelteflow.dev/package.json | 50 +- sites/svelteflow.dev/src/global.css | 17 + .../src/layouts/example-with-frontmatter.tsx | 29 - .../{sites => layouts}/examples/overview.tsx | 3 +- .../{sites => layouts}/home/flows/flow-a.tsx | 0 .../{sites => layouts}/home/flows/flow-b.tsx | 0 .../{sites => layouts}/home/flows/flow-c.tsx | 0 .../src/{sites => layouts}/home/index.tsx | 4 +- .../src/{sites => layouts}/showcase.tsx | 0 .../svelte-flow-support.tsx | 0 .../src/{sites => layouts}/whats-new.tsx | 0 sites/svelteflow.dev/src/pages/_app.tsx | 33 +- .../api-reference/components/background.mdx | 2 +- .../api-reference/components/base-edge.mdx | 2 +- .../components/control-button.mdx | 2 +- .../api-reference/components/controls.mdx | 2 +- .../components/edge-label-renderer.mdx | 2 +- .../api-reference/components/edge-label.mdx | 2 +- .../pages/api-reference/components/handle.mdx | 2 +- .../api-reference/components/mini-map.mdx | 2 +- .../components/node-resize-control.mdx | 2 +- .../api-reference/components/node-resizer.mdx | 7 +- .../api-reference/components/node-toolbar.mdx | 2 +- .../pages/api-reference/components/panel.mdx | 2 +- .../api-reference/hooks/use-connection.mdx | 2 +- .../pages/api-reference/hooks/use-edges.mdx | 2 +- .../hooks/use-handle-connections.mdx | 2 +- .../api-reference/hooks/use-internal-node.mdx | 2 +- .../api-reference/hooks/use-nodes-data.mdx | 2 +- .../hooks/use-nodes-initialized.mdx | 2 +- .../pages/api-reference/hooks/use-nodes.mdx | 2 +- .../pages/api-reference/hooks/use-store.mdx | 2 +- .../api-reference/hooks/use-svelte-flow.mdx | 2 +- .../hooks/use-update-node-internals.mdx | 2 +- .../src/pages/api-reference/svelte-flow.mdx | 2 +- .../pages/api-reference/types/connection.mdx | 2 +- .../types/default-edge-options.mdx | 2 +- .../pages/api-reference/types/edge-marker.mdx | 2 +- .../pages/api-reference/types/edge-props.mdx | 2 +- .../src/pages/api-reference/types/edge.mdx | 2 +- .../api-reference/types/fit-view-options.mdx | 2 +- .../api-reference/types/internal-node.mdx | 2 +- .../api-reference/types/key-definition.mdx | 2 +- .../pages/api-reference/types/node-props.mdx | 2 +- .../src/pages/api-reference/types/node.mdx | 2 +- .../api-reference/types/svelte-flow-store.mdx | 2 +- .../pages/api-reference/types/viewport.mdx | 2 +- .../pages/api-reference/utils/add-edge.mdx | 2 +- .../api-reference/utils/get-bezier-path.mdx | 2 +- .../utils/get-connected-edges.mdx | 2 +- .../api-reference/utils/get-incomers.mdx | 2 +- .../api-reference/utils/get-nodes-bounds.mdx | 2 +- .../api-reference/utils/get-outgoers.mdx | 2 +- .../utils/get-simple-bezier-path.mdx | 2 +- .../utils/get-smooth-step-path.mdx | 2 +- .../api-reference/utils/get-straight-path.mdx | 2 +- .../utils/get-viewport-for-bounds.mdx | 2 +- .../src/pages/api-reference/utils/is-edge.mdx | 2 +- .../src/pages/api-reference/utils/is-node.mdx | 2 +- .../examples/edges/custom-connectionline.mdx | 10 +- .../src/pages/examples/edges/custom-edges.mdx | 10 +- .../examples/edges/edge-label-renderer.mdx | 10 +- .../src/pages/examples/edges/edge-markers.mdx | 10 +- .../src/pages/examples/edges/edge-types.mdx | 10 +- .../examples/edges/simple-floating-edges.mdx | 10 +- .../src/pages/examples/index.mdx | 2 +- .../examples/interaction/computing-flows.mdx | 9 +- .../examples/interaction/context-menu.mdx | 9 +- .../examples/interaction/contextual-zoom.mdx | 10 +- .../examples/interaction/drag-and-drop.mdx | 10 +- .../pages/examples/interaction/validation.mdx | 12 +- .../src/pages/examples/layout/dagre.mdx | 12 +- .../src/pages/examples/layout/elkjs.mdx | 10 +- .../pages/examples/layout/horizontal-flow.mdx | 12 +- .../src/pages/examples/layout/subflows.mdx | 13 +- .../pages/examples/misc/download-image.mdx | 10 +- .../src/pages/examples/misc/threlte-flow.mdx | 12 +- .../pages/examples/misc/use-svelte-flow.mdx | 9 +- .../examples/nodes/add-node-on-edge-drop.mdx | 10 +- .../pages/examples/nodes/connection-limit.mdx | 12 +- .../src/pages/examples/nodes/custom-node.mdx | 10 +- .../src/pages/examples/nodes/drag-handle.mdx | 10 +- .../src/pages/examples/nodes/easy-connect.mdx | 10 +- .../pages/examples/nodes/intersections.mdx | 10 +- .../src/pages/examples/nodes/node-resizer.mdx | 10 +- .../examples/nodes/proximity-connect.mdx | 10 +- .../src/pages/examples/nodes/stress.mdx | 10 +- .../src/pages/examples/nodes/update-node.mdx | 12 +- .../src/pages/examples/overview.mdx | 12 +- .../src/pages/examples/styling/base-style.mdx | 10 +- .../src/pages/examples/styling/dark-mode.mdx | 10 +- .../src/pages/examples/styling/tailwind.mdx | 8 +- .../src/pages/examples/styling/turbo-flow.mdx | 8 +- sites/svelteflow.dev/src/pages/index.mdx | 2 +- .../learn/getting-started/building-a-flow.mdx | 19 +- .../learn/getting-started/installation.mdx | 29 +- .../src/pages/learn/guides/custom-nodes.mdx | 13 +- .../src/pages/learn/guides/sub-flows.mdx | 17 +- .../src/pages/learn/guides/theming.mdx | 11 +- .../svelteflow.dev/src/pages/learn/index.mdx | 31 +- sites/svelteflow.dev/src/pages/showcase.mdx | 2 +- sites/svelteflow.dev/src/pages/support-us.mdx | 2 +- sites/svelteflow.dev/src/pages/whats-new.mdx | 6 +- .../reference => references}/SvelteFlow.ts | 0 .../components/Background.ts | 0 .../components/BaseEdge.ts | 0 .../components/Controls.ts | 0 .../components/EdgeLabel.ts | 0 .../components/EdgeLabelRenderer.ts | 0 .../components/Handle.ts | 0 .../components/MiniMap.ts | 0 .../components/NodeResizeControl.ts | 0 .../components/NodeResizer.ts | 0 .../components/NodeToolbar.ts | 0 .../components/Panel.ts | 0 .../hooks/useConnection.ts | 0 .../hooks/useEdges.ts | 0 .../hooks/useHandleConnections.ts | 0 .../hooks/useInternalNode.ts | 0 .../hooks/useNodes.ts | 0 .../hooks/useNodesData.ts | 0 .../hooks/useNodesInitialized.ts | 0 .../hooks/useStore.ts | 0 .../hooks/useSvelteFlow.ts | 0 .../hooks/useUpdateNodeInternals.ts | 0 .../types/BackgroundVariant.ts | 0 .../types/Connection.fields.ts | 0 .../types/DefaultEdgeOptions.fields.ts | 0 .../types/Edge.fields.ts | 0 .../types/EdgeMarker.ts | 0 .../types/EdgeProps.ts | 0 .../types/FitViewOptions.ts | 0 .../types/HandleElement.fields.ts | 0 .../types/Node.fields.ts | 0 .../types/NodeProps.fields.ts | 0 .../types/SvelteFlowStore.ts | 0 .../types/Viewport.fields.ts | 0 .../reference => references}/utils/addEdge.ts | 0 .../utils/getBezierPath.ts | 0 .../utils/getConnectedEdges.ts | 0 .../utils/getIncomers.ts | 0 .../utils/getOutgoers.ts | 0 .../utils/getRectOfNodes.ts | 0 .../utils/getSimpleBezierPath.ts | 0 .../utils/getSmoothStepPath.ts | 0 .../utils/getStraightPath.ts | 0 .../utils/getViewportForBounds.ts | 0 .../reference => references}/utils/isEdge.ts | 0 .../reference => references}/utils/isNode.ts | 0 .../utils/updateEdge.ts | 0 .../src/styles/fonts/NTDapper-black.woff2 | Bin 25336 -> 0 bytes .../src/styles/fonts/NTDapper-bold.woff2 | Bin 25652 -> 0 bytes .../src/styles/fonts/NTDapper-medium.woff2 | Bin 25584 -> 0 bytes .../src/styles/fonts/NTDapper-regular.woff2 | Bin 24676 -> 0 bytes sites/svelteflow.dev/src/styles/global.css | 63 - .../api-reference-by-category.ts | 2 +- .../get-static-props/github-npm-stats.ts | 25 +- .../mdx-content-under-route.ts | 50 - sites/svelteflow.dev/src/utils/index.ts | 27 +- sites/svelteflow.dev/theme.config.tsx | 100 +- sites/xyflow.com/.env | 3 + sites/xyflow.com/.env.development | 3 +- sites/xyflow.com/next-env.d.ts | 2 +- sites/xyflow.com/next.config.mjs | 5 +- sites/xyflow.com/package.json | 50 +- .../react-flow-components/db-node-demo.png | Bin 0 -> 591483 bytes .../img/blog/react-flow-components/header.png | Bin 0 -> 1478726 bytes .../blog/react-flow-components/multiverse.png | Bin 0 -> 710548 bytes .../zoom-slider-demo.png | Bin 0 -> 472382 bytes sites/xyflow.com/src/global.css | 18 + .../src/{sites => layouts}/about.tsx | 0 .../layouts/blog-post-with-frontmatter.tsx | 48 - .../src/{sites => layouts}/blog.tsx | 2 +- .../src/{sites => layouts}/careers.tsx | 0 .../src/{sites => layouts}/contact.tsx | 0 .../src/{sites => layouts}/home.tsx | 0 .../src/{sites => layouts}/open-source.tsx | 0 sites/xyflow.com/src/pages/_app.tsx | 34 +- sites/xyflow.com/src/pages/about.mdx | 2 +- sites/xyflow.com/src/pages/blog.mdx | 2 +- sites/xyflow.com/src/pages/blog/_meta.ts | 2 + .../blog/asking-for-money-for-open-source.mdx | 2 +- .../src/pages/blog/react-flow-12-release.mdx | 12 +- .../src/pages/blog/react-flow-components.mdx | 160 + .../blog/react-flow-developer-survey-2023.mdx | 2 +- .../src/pages/blog/react-flow-fall-2022.mdx | 2 +- .../react-flow-pro-platform-open-source.mdx | 2 +- .../src/pages/blog/react-flow-v-11-5.mdx | 2 +- .../src/pages/blog/react-flow-v10.mdx | 9 +- .../src/pages/blog/react-flow-v11.mdx | 4 +- .../src/pages/blog/react-flow-winter-2022.mdx | 2 +- .../pages/blog/reactflow-npm-package-name.mdx | 2 +- .../src/pages/blog/spring-update-2023.mdx | 2 +- .../pages/blog/svelte-flow-alpha-xyflow.mdx | 4 +- .../src/pages/blog/svelte-flow-launch.mdx | 4 +- .../update-react-flow-12-svelte-flow-1.mdx | 9 +- .../src/pages/blog/why-svelte-flow.mdx | 2 +- sites/xyflow.com/src/pages/careers.mdx | 2 +- sites/xyflow.com/src/pages/contact.mdx | 2 +- sites/xyflow.com/src/pages/index.mdx | 2 +- sites/xyflow.com/src/pages/open-source.mdx | 2 +- .../src/styles/fonts/NTDapper-black.woff2 | Bin 25336 -> 0 bytes .../src/styles/fonts/NTDapper-bold.woff2 | Bin 25652 -> 0 bytes .../src/styles/fonts/NTDapper-medium.woff2 | Bin 25584 -> 0 bytes .../src/styles/fonts/NTDapper-regular.woff2 | Bin 24676 -> 0 bytes sites/xyflow.com/src/styles/global.css | 27 - sites/xyflow.com/src/utils/index.ts | 27 +- sites/xyflow.com/theme.config.tsx | 55 +- turbo.json | 64 +- 696 files changed, 13936 insertions(+), 19539 deletions(-) create mode 100644 apps/ui-components/.eslintignore create mode 100644 apps/ui-components/.eslintrc.json create mode 100644 apps/ui-components/.gitignore create mode 100644 apps/ui-components/.prettierrc create mode 100644 apps/ui-components/README.md create mode 100644 apps/ui-components/app/components/animated-svg-edge/page.tsx create mode 100644 apps/ui-components/app/components/annotation-node/page.tsx create mode 100644 apps/ui-components/app/components/base-handle/page.tsx create mode 100644 apps/ui-components/app/components/base-node/page.tsx create mode 100644 apps/ui-components/app/components/button-edge/page.tsx create mode 100644 apps/ui-components/app/components/database-schema-node/page.tsx create mode 100644 apps/ui-components/app/components/devtools/page.tsx create mode 100644 apps/ui-components/app/components/labeled-group-node/page.tsx create mode 100644 apps/ui-components/app/components/labeled-handle/page.tsx create mode 100644 apps/ui-components/app/components/placeholder-node/page.tsx create mode 100644 apps/ui-components/app/components/tooltip-node/page.tsx create mode 100644 apps/ui-components/app/components/zoom-select/page.tsx create mode 100644 apps/ui-components/app/components/zoom-slider/page.tsx create mode 100644 apps/ui-components/app/globals.css create mode 100644 apps/ui-components/app/layout.tsx create mode 100644 apps/ui-components/app/page.tsx create mode 100644 apps/ui-components/components.json create mode 100644 apps/ui-components/components/demo-wrapper.tsx create mode 100644 apps/ui-components/components/theme-switcher.tsx create mode 100644 apps/ui-components/components/ui/button.tsx create mode 100644 apps/ui-components/components/ui/select.tsx create mode 100644 apps/ui-components/components/ui/slider.tsx create mode 100644 apps/ui-components/components/ui/table.tsx create mode 100644 apps/ui-components/components/ui/toggle-group.tsx create mode 100644 apps/ui-components/components/ui/toggle.tsx create mode 100644 apps/ui-components/lib/utils.ts create mode 100644 apps/ui-components/middleware.ts create mode 100644 apps/ui-components/next.config.js create mode 100644 apps/ui-components/package.json create mode 100644 apps/ui-components/postcss.config.mjs create mode 100644 apps/ui-components/registry/components/animated-svg-edge/demo.tsx create mode 100644 apps/ui-components/registry/components/animated-svg-edge/index.tsx create mode 100644 apps/ui-components/registry/components/animated-svg-edge/registry.json create mode 100644 apps/ui-components/registry/components/annotation-node/demo.tsx create mode 100644 apps/ui-components/registry/components/annotation-node/index.tsx create mode 100644 apps/ui-components/registry/components/annotation-node/registry.json create mode 100644 apps/ui-components/registry/components/base-handle/demo.tsx create mode 100644 apps/ui-components/registry/components/base-handle/index.tsx create mode 100644 apps/ui-components/registry/components/base-handle/registry.json create mode 100644 apps/ui-components/registry/components/base-node/demo.tsx create mode 100644 apps/ui-components/registry/components/base-node/index.tsx create mode 100644 apps/ui-components/registry/components/base-node/registry.json create mode 100644 apps/ui-components/registry/components/button-edge/demo.tsx create mode 100644 apps/ui-components/registry/components/button-edge/index.tsx create mode 100644 apps/ui-components/registry/components/button-edge/registry.json create mode 100644 apps/ui-components/registry/components/database-schema-node/demo.tsx create mode 100644 apps/ui-components/registry/components/database-schema-node/index.tsx create mode 100644 apps/ui-components/registry/components/database-schema-node/registry.json create mode 100644 apps/ui-components/registry/components/devtools/demo.tsx create mode 100644 apps/ui-components/registry/components/devtools/index.tsx create mode 100644 apps/ui-components/registry/components/devtools/registry.json create mode 100644 apps/ui-components/registry/components/labeled-group-node/demo.tsx create mode 100644 apps/ui-components/registry/components/labeled-group-node/index.tsx create mode 100644 apps/ui-components/registry/components/labeled-group-node/registry.json create mode 100644 apps/ui-components/registry/components/labeled-handle/demo.tsx create mode 100644 apps/ui-components/registry/components/labeled-handle/index.tsx create mode 100644 apps/ui-components/registry/components/labeled-handle/registry.json create mode 100644 apps/ui-components/registry/components/placeholder-node/demo.tsx create mode 100644 apps/ui-components/registry/components/placeholder-node/index.tsx create mode 100644 apps/ui-components/registry/components/placeholder-node/registry.json create mode 100644 apps/ui-components/registry/components/tooltip-node/demo.tsx create mode 100644 apps/ui-components/registry/components/tooltip-node/index.tsx create mode 100644 apps/ui-components/registry/components/tooltip-node/registry.json create mode 100644 apps/ui-components/registry/components/zoom-select/demo.tsx create mode 100644 apps/ui-components/registry/components/zoom-select/index.tsx create mode 100644 apps/ui-components/registry/components/zoom-select/registry.json create mode 100644 apps/ui-components/registry/components/zoom-slider/demo.tsx create mode 100644 apps/ui-components/registry/components/zoom-slider/index.tsx create mode 100644 apps/ui-components/registry/components/zoom-slider/registry.json create mode 100644 apps/ui-components/scripts/generate-registry.js create mode 100644 apps/ui-components/scripts/init-new-component.js create mode 100644 apps/ui-components/tailwind.config.ts create mode 100644 apps/ui-components/templates/component/demo.tsx create mode 100644 apps/ui-components/templates/component/index.tsx create mode 100644 apps/ui-components/templates/component/registry.json create mode 100644 apps/ui-components/templates/page/page.tsx create mode 100644 apps/ui-components/tsconfig.json delete mode 100644 apps/xy-styleguide/.eslintrc.cjs delete mode 100644 apps/xy-styleguide/.gitignore delete mode 100644 apps/xy-styleguide/.storybook/main.ts delete mode 100644 apps/xy-styleguide/.storybook/preview-head.html delete mode 100644 apps/xy-styleguide/.storybook/preview.ts delete mode 100644 apps/xy-styleguide/index.html delete mode 100644 apps/xy-styleguide/package.json delete mode 100644 apps/xy-styleguide/postcss.config.js delete mode 100644 apps/xy-styleguide/public/vite.svg delete mode 100644 apps/xy-styleguide/src/App.css delete mode 100644 apps/xy-styleguide/src/App.tsx delete mode 100644 apps/xy-styleguide/src/assets/react.svg delete mode 100644 apps/xy-styleguide/src/index.css delete mode 100644 apps/xy-styleguide/src/main.tsx delete mode 100644 apps/xy-styleguide/src/stories/Accordion.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/Button.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/Checkbox.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/Container.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/Introduction.mdx delete mode 100644 apps/xy-styleguide/src/stories/ListWrapper.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/PricingTable.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/RadioGroup.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/Select.stories.tsx delete mode 100644 apps/xy-styleguide/src/stories/assets/code-brackets.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/colors.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/comments.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/direction.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/flow.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/plugin.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/repo.svg delete mode 100644 apps/xy-styleguide/src/stories/assets/stackalt.svg delete mode 100644 apps/xy-styleguide/src/styles/tailwind.css delete mode 100644 apps/xy-styleguide/src/vite-env.d.ts delete mode 100644 apps/xy-styleguide/tailwind.config.js delete mode 100644 apps/xy-styleguide/tsconfig.json delete mode 100644 apps/xy-styleguide/tsconfig.node.json delete mode 100644 apps/xy-styleguide/vite.config.ts rename {packages => configs}/eslint-config-xyflow/index.js (100%) create mode 100644 configs/eslint-config-xyflow/package.json rename {packages => configs}/xy-tailwind-config/index.js (100%) rename {packages => configs}/xy-tailwind-config/package.json (83%) rename {packages => configs}/xy-tsconfig/base.json (100%) rename {packages => configs}/xy-tsconfig/nextjs.json (100%) rename {packages => configs}/xy-tsconfig/package.json (100%) rename {packages => configs}/xy-tsconfig/react-library.json (100%) delete mode 100644 packages/eslint-config-xyflow/package.json delete mode 100644 packages/xy-shared/components/head.tsx create mode 100644 packages/xy-shared/context/shared-context.tsx rename {apps/xy-styleguide/public => packages/xy-shared}/fonts/NTDapper-black.woff2 (100%) rename {apps/xy-styleguide/public => packages/xy-shared}/fonts/NTDapper-bold.woff2 (100%) rename {apps/xy-styleguide/public => packages/xy-shared}/fonts/NTDapper-medium.woff2 (100%) rename {apps/xy-styleguide/public => packages/xy-shared}/fonts/NTDapper-regular.woff2 (100%) create mode 100644 packages/xy-shared/fonts/index.ts create mode 100644 packages/xy-shared/layouts/blog-post-base.tsx create mode 100644 packages/xy-shared/layouts/tutorial.tsx rename packages/xy-shared/lib/{utils.tsx => index.tsx} (73%) create mode 100644 packages/xy-shared/server/compile-code-snippet.ts create mode 100644 packages/xy-shared/server/get-static-code.ts create mode 100644 packages/xy-shared/server/index.ts rename {sites/reactflow.dev/src/utils/get-static-props => packages/xy-shared/server}/mdx-content-under-route.ts (86%) create mode 100644 packages/xy-shared/server/utils.ts rename packages/xy-shared/{lib => styles}/globals.css (79%) create mode 100644 packages/xy-shared/types/index.tsx create mode 100644 packages/xy-shared/widgets/remote-code-viewer/fetchFiles.ts create mode 100644 packages/xy-shared/widgets/remote-code-viewer/style.css delete mode 100644 scripts/create-whats-new/index.js delete mode 100644 scripts/create-whats-new/package.json delete mode 100644 scripts/create-whats-new/whats-new-creator.js create mode 100644 sites/reactflow.dev/src/components/ui-component-viewer.tsx create mode 100644 sites/reactflow.dev/src/global.css delete mode 100644 sites/reactflow.dev/src/layouts/case-study-with-metadata.tsx delete mode 100644 sites/reactflow.dev/src/layouts/example-with-frontmatter.tsx rename sites/reactflow.dev/src/{sites => layouts}/examples/overview.tsx (98%) rename sites/reactflow.dev/src/{sites => layouts}/home/flows/flow-a.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/home/flows/flow-b.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/home/flows/flow-c.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/home/index.tsx (98%) rename sites/reactflow.dev/src/{sites => layouts}/pro/case-studies.tsx (91%) rename sites/reactflow.dev/src/{sites => layouts}/pro/enterprise.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/pro/examples.tsx (98%) rename sites/reactflow.dev/src/{sites => layouts}/pro/index.tsx (97%) rename sites/reactflow.dev/src/{sites => layouts}/pro/pricing.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/pro/quote-request.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/showcase.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/surveys/flow/edges.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/surveys/flow/hooks.ts (100%) rename sites/reactflow.dev/src/{sites => layouts}/surveys/flow/nodes.tsx (100%) rename sites/reactflow.dev/src/{sites => layouts}/surveys/reactflow-2023.tsx (100%) delete mode 100644 sites/reactflow.dev/src/layouts/tutorial-with-frontmatter.tsx rename sites/reactflow.dev/src/{sites => layouts}/tutorials.tsx (89%) rename sites/reactflow.dev/src/{sites => layouts}/whats-new.tsx (100%) create mode 100644 sites/reactflow.dev/src/pages/components/_meta.tsx create mode 100644 sites/reactflow.dev/src/pages/components/controls/_meta.tsx create mode 100644 sites/reactflow.dev/src/pages/components/controls/zoom-select.mdx create mode 100644 sites/reactflow.dev/src/pages/components/controls/zoom-slider.mdx create mode 100644 sites/reactflow.dev/src/pages/components/edges/_meta.tsx create mode 100644 sites/reactflow.dev/src/pages/components/edges/animated-svg-edge.mdx create mode 100644 sites/reactflow.dev/src/pages/components/edges/button-edge.mdx create mode 100644 sites/reactflow.dev/src/pages/components/handles/_meta.tsx create mode 100644 sites/reactflow.dev/src/pages/components/handles/base-handle.mdx create mode 100644 sites/reactflow.dev/src/pages/components/handles/labeled-handle.mdx create mode 100644 sites/reactflow.dev/src/pages/components/index.mdx create mode 100644 sites/reactflow.dev/src/pages/components/misc/devtools.mdx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/_meta.tsx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/annotation-node.mdx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/base-node.mdx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/database-schema-node.mdx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/labeled-group-node.mdx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/placeholder-node.mdx create mode 100644 sites/reactflow.dev/src/pages/components/nodes/tooltip-node.mdx create mode 100644 sites/reactflow.dev/src/pages/learn/concepts/built-in-components.mdx delete mode 100644 sites/reactflow.dev/src/pages/learn/concepts/plugin-components.mdx create mode 100644 sites/reactflow.dev/src/pages/whats-new/2024-11-04.mdx rename sites/reactflow.dev/src/{page-data/reference => references}/ReactFlow.props.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/Background.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/BaseEdge.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/Controls.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/EdgeLabelRenderer.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/EdgeText.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/Handle.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/MiniMap.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/NodeResizeControl.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/NodeResizer.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/NodeToolbar.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/components/Panel.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useConnection.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useEdges.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useEdgesState.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useHandleConnections.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useInternalNode.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useKeyPress.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useNodeId.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useNodes.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useNodesData.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useNodesInitialized.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useNodesState.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useOnSelectionChange.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useOnViewportChange.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useReactFlow.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useStore.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useStoreApi.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useUpdateNodeInternals.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/hooks/useViewport.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/BackgroundVariant.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/Connection.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/ConnectionLineComponentProps.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/ConnectionState.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/DefaultEdgeOptions.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/Edge.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/EdgeChange.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/EdgeMarker.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/EdgeProps.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/FitViewOptions.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/Handle.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/HandleConnection.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/InternalNode.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/MiniMapNodeProps.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/Node.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/NodeChange.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/NodeProps.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/OnEdgesChange.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/OnNodesChange.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/ReactFlowInstance.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/ReactFlowJsonObject.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/ResizeParams.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/types/Viewport.fields.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/addEdge.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/applyEdgeChanges.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/applyNodeChanges.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getBezierPath.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getConnectedEdges.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getIncomers.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getMarkerEnd.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getOutgoers.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getRectOfNodes.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getSimpleBezierPath.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getSmoothStepPath.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getStraightPath.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getTransformForBounds.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/getViewportForBounds.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/isEdge.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/isNode.ts (100%) rename sites/reactflow.dev/src/{page-data/reference => references}/utils/reconnectEdge.ts (100%) delete mode 100644 sites/reactflow.dev/src/styles/fonts/NTDapper-black.woff2 delete mode 100644 sites/reactflow.dev/src/styles/fonts/NTDapper-bold.woff2 delete mode 100644 sites/reactflow.dev/src/styles/fonts/NTDapper-medium.woff2 delete mode 100644 sites/reactflow.dev/src/styles/fonts/NTDapper-regular.woff2 delete mode 100644 sites/reactflow.dev/src/styles/global.css create mode 100644 sites/reactflow.dev/src/utils/get-static-props/fetch-shadcn-component.ts create mode 100644 sites/svelteflow.dev/src/global.css delete mode 100644 sites/svelteflow.dev/src/layouts/example-with-frontmatter.tsx rename sites/svelteflow.dev/src/{sites => layouts}/examples/overview.tsx (98%) rename sites/svelteflow.dev/src/{sites => layouts}/home/flows/flow-a.tsx (100%) rename sites/svelteflow.dev/src/{sites => layouts}/home/flows/flow-b.tsx (100%) rename sites/svelteflow.dev/src/{sites => layouts}/home/flows/flow-c.tsx (100%) rename sites/svelteflow.dev/src/{sites => layouts}/home/index.tsx (98%) rename sites/svelteflow.dev/src/{sites => layouts}/showcase.tsx (100%) rename sites/svelteflow.dev/src/{sites => layouts}/svelte-flow-support.tsx (100%) rename sites/svelteflow.dev/src/{sites => layouts}/whats-new.tsx (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/SvelteFlow.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/Background.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/BaseEdge.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/Controls.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/EdgeLabel.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/EdgeLabelRenderer.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/Handle.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/MiniMap.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/NodeResizeControl.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/NodeResizer.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/NodeToolbar.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/components/Panel.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useConnection.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useEdges.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useHandleConnections.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useInternalNode.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useNodes.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useNodesData.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useNodesInitialized.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useStore.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useSvelteFlow.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/hooks/useUpdateNodeInternals.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/BackgroundVariant.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/Connection.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/DefaultEdgeOptions.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/Edge.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/EdgeMarker.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/EdgeProps.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/FitViewOptions.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/HandleElement.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/Node.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/NodeProps.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/SvelteFlowStore.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/types/Viewport.fields.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/addEdge.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getBezierPath.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getConnectedEdges.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getIncomers.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getOutgoers.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getRectOfNodes.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getSimpleBezierPath.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getSmoothStepPath.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getStraightPath.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/getViewportForBounds.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/isEdge.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/isNode.ts (100%) rename sites/svelteflow.dev/src/{page-data/reference => references}/utils/updateEdge.ts (100%) delete mode 100644 sites/svelteflow.dev/src/styles/fonts/NTDapper-black.woff2 delete mode 100644 sites/svelteflow.dev/src/styles/fonts/NTDapper-bold.woff2 delete mode 100644 sites/svelteflow.dev/src/styles/fonts/NTDapper-medium.woff2 delete mode 100644 sites/svelteflow.dev/src/styles/fonts/NTDapper-regular.woff2 delete mode 100644 sites/svelteflow.dev/src/styles/global.css delete mode 100644 sites/svelteflow.dev/src/utils/get-static-props/mdx-content-under-route.ts create mode 100644 sites/xyflow.com/.env create mode 100644 sites/xyflow.com/public/img/blog/react-flow-components/db-node-demo.png create mode 100644 sites/xyflow.com/public/img/blog/react-flow-components/header.png create mode 100644 sites/xyflow.com/public/img/blog/react-flow-components/multiverse.png create mode 100644 sites/xyflow.com/public/img/blog/react-flow-components/zoom-slider-demo.png create mode 100644 sites/xyflow.com/src/global.css rename sites/xyflow.com/src/{sites => layouts}/about.tsx (100%) delete mode 100644 sites/xyflow.com/src/layouts/blog-post-with-frontmatter.tsx rename sites/xyflow.com/src/{sites => layouts}/blog.tsx (94%) rename sites/xyflow.com/src/{sites => layouts}/careers.tsx (100%) rename sites/xyflow.com/src/{sites => layouts}/contact.tsx (100%) rename sites/xyflow.com/src/{sites => layouts}/home.tsx (100%) rename sites/xyflow.com/src/{sites => layouts}/open-source.tsx (100%) create mode 100644 sites/xyflow.com/src/pages/blog/react-flow-components.mdx delete mode 100644 sites/xyflow.com/src/styles/fonts/NTDapper-black.woff2 delete mode 100644 sites/xyflow.com/src/styles/fonts/NTDapper-bold.woff2 delete mode 100644 sites/xyflow.com/src/styles/fonts/NTDapper-medium.woff2 delete mode 100644 sites/xyflow.com/src/styles/fonts/NTDapper-regular.woff2 delete mode 100644 sites/xyflow.com/src/styles/global.css diff --git a/apps/example-apps/package.json b/apps/example-apps/package.json index 766a33fa8..d19c06fd1 100644 --- a/apps/example-apps/package.json +++ b/apps/example-apps/package.json @@ -10,28 +10,28 @@ "screenshots": "concurrently \"vite dev\" \"node scripts/generate-screenshots.js\"" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.1.1", + "@sveltejs/vite-plugin-svelte": "^3.1.2", "@tsconfig/svelte": "^5.0.4", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", - "@types/react": "^18.3.4", - "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^4.2.1", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.3", "concurrently": "^9.0.1", "cors": "^2.8.5", - "express": "^4.20.0", + "express": "^4.21.1", "glob": "^11.0.0", - "picocolors": "^1.1.0", - "svelte-check": "^3.8.5", - "tslib": "^2.6.3", - "typescript": "^5.5.3", - "vite": "^5.4.1" + "picocolors": "^1.1.1", + "svelte-check": "^4.0.5", + "tslib": "^2.8.0", + "typescript": "^5.5.4", + "vite": "^5.4.10" }, "dependencies": { - "@dagrejs/dagre": "^1.1.1", - "@threlte/core": "^6.1.0", - "@xyflow/react": "^12.1.1", - "@xyflow/svelte": "^0.1.16", + "@dagrejs/dagre": "^1.1.4", + "@threlte/core": "^7.3.1", + "@xyflow/react": "^12.3.2", + "@xyflow/svelte": "^0.1.21", "d3-drag": "^3.0.0", "d3-force": "^3.0.0", "d3-hierarchy": "^3.1.2", @@ -40,17 +40,17 @@ "elkjs": "^0.9.3", "entitree-flex": "^0.4.1", "html-to-image": "^1.11.11", - "nanoid": "^4.0.2", + "nanoid": "^5.0.7", "puppeteer": "^22.7.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-icons": "^4.10.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^5.3.0", "react-remark": "^2.1.0", - "styled-components": "^6.1.11", - "svelte": "^4.2.18", - "svelte-feather-icons": "^4.0.1", + "styled-components": "^6.1.13", + "svelte": "^4.2.19", + "svelte-feather-icons": "^4.1.0", "twind": "^0.16.19", "webcola": "^3.4.0", - "zustand": "5.0.0-rc.2" + "zustand": "5.0.0" } } diff --git a/apps/example-apps/react/learn/devtools/NodeInspector.tsx b/apps/example-apps/react/learn/devtools/NodeInspector.tsx index c32d86f69..cc322b6eb 100644 --- a/apps/example-apps/react/learn/devtools/NodeInspector.tsx +++ b/apps/example-apps/react/learn/devtools/NodeInspector.tsx @@ -1,33 +1,41 @@ -import { useNodes, EdgeLabelRenderer } from '@xyflow/react'; +import { + useNodes, + ViewportPortal, + useReactFlow, + type XYPosition, +} from '@xyflow/react'; export default function NodeInspector() { + const { getInternalNode } = useReactFlow(); const nodes = useNodes(); return ( - +
{nodes.map((node) => { - const x = node.position.x || 0; - const y = node.position.y || 0; - const width = node.width || 0; - const height = node.height || 0; + const internalNode = getInternalNode(node.id); + if (!internalNode) { + return null; + } + + const absPosition = internalNode?.internals.positionAbsolute; return ( ); })}
-
+ ); } @@ -35,8 +43,8 @@ type NodeInfoProps = { id: string; type: string; selected: boolean; - x: number; - y: number; + position: XYPosition; + absPosition: XYPosition; width?: number; height?: number; data: any; @@ -46,8 +54,8 @@ function NodeInfo({ id, type, selected, - x, - y, + position, + absPosition, width, height, data, @@ -61,7 +69,7 @@ function NodeInfo({ className="react-flow__devtools-nodeinfo" style={{ position: 'absolute', - transform: `translate(${x}px, ${y + height}px)`, + transform: `translate(${absPosition.x}px, ${absPosition.y + height}px)`, width: width * 2, }} > @@ -69,7 +77,7 @@ function NodeInfo({
type: {type}
selected: {selected ? 'true' : 'false'}
- position: {x.toFixed(1)}, {y.toFixed(1)} + position: {position.x.toFixed(1)}, {position.y.toFixed(1)}
dimensions: {width} × {height} diff --git a/apps/example-apps/react/learn/devtools/ViewportLogger.tsx b/apps/example-apps/react/learn/devtools/ViewportLogger.tsx index f50e3aee0..098b0c292 100644 --- a/apps/example-apps/react/learn/devtools/ViewportLogger.tsx +++ b/apps/example-apps/react/learn/devtools/ViewportLogger.tsx @@ -8,5 +8,5 @@ export default function ViewportLogger() { )}, zoom: ${s.transform[2].toFixed(2)}`, ); - return {viewport}; + return {viewport}; } diff --git a/apps/ui-components/.eslintignore b/apps/ui-components/.eslintignore new file mode 100644 index 000000000..64f45cc5c --- /dev/null +++ b/apps/ui-components/.eslintignore @@ -0,0 +1 @@ +templates/** \ No newline at end of file diff --git a/apps/ui-components/.eslintrc.json b/apps/ui-components/.eslintrc.json new file mode 100644 index 000000000..bffb357a7 --- /dev/null +++ b/apps/ui-components/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/apps/ui-components/.gitignore b/apps/ui-components/.gitignore new file mode 100644 index 000000000..3709f54ec --- /dev/null +++ b/apps/ui-components/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +public/registry/** +public/demo/** \ No newline at end of file diff --git a/apps/ui-components/.prettierrc b/apps/ui-components/.prettierrc new file mode 100644 index 000000000..b4bfed357 --- /dev/null +++ b/apps/ui-components/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/apps/ui-components/README.md b/apps/ui-components/README.md new file mode 100644 index 000000000..12959619c --- /dev/null +++ b/apps/ui-components/README.md @@ -0,0 +1,37 @@ +# React Flow components for shadcn/cli + +This application is used for developing and publishing new components that can be installed by running the shadcn cli. + +## Adding components + +```bash +npx shadcn init # initialize shadcn +npx shadcn add http://ui.reactflow.dev/.json ## adding component +``` + +## Developing new components + +We have an initialization script that can be run via + +```bash +pnpm add-component +``` + +This adds the following files: + +```bash +components/ +├─ xy/ +│ ├─ / +│ │ ├─ index.tsx # source code of the component +│ │ ├─ demo.tsx # small demo showing usage to appear on website +│ │ ├─ registry.json # shadcn configuration +app/ +├─ / +│ ├─ page.tsx # route for rendering component + +``` + +## Deploying components + +Inside the `scripts/` folder you will find a script called `generate-registry.js`. This script is executed when running `pnpm build`. It generates registry files for the cli (`regsitry/.json`), as well as a `demo/.json` file with the example code for each component and saves them inside the `public/` folder. This way by building and deploying the app we can directly host all neccessary files needed for the cli and integrating it with our website. diff --git a/apps/ui-components/app/components/animated-svg-edge/page.tsx b/apps/ui-components/app/components/animated-svg-edge/page.tsx new file mode 100644 index 000000000..b45b439e1 --- /dev/null +++ b/apps/ui-components/app/components/animated-svg-edge/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/animated-svg-edge/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/annotation-node/page.tsx b/apps/ui-components/app/components/annotation-node/page.tsx new file mode 100644 index 000000000..b235b3cc1 --- /dev/null +++ b/apps/ui-components/app/components/annotation-node/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/annotation-node/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/base-handle/page.tsx b/apps/ui-components/app/components/base-handle/page.tsx new file mode 100644 index 000000000..eb31c9ebb --- /dev/null +++ b/apps/ui-components/app/components/base-handle/page.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { Background, ReactFlow } from "@xyflow/react"; +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/base-handle/demo"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: {}, + type: "customNode", + }, +]; + +const nodeTypes = { + customNode: Demo, +}; + +export default function DemoPage() { + return ( + +
+ + + +
+
+ ); +} diff --git a/apps/ui-components/app/components/base-node/page.tsx b/apps/ui-components/app/components/base-node/page.tsx new file mode 100644 index 000000000..78c6fee03 --- /dev/null +++ b/apps/ui-components/app/components/base-node/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/base-node/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/button-edge/page.tsx b/apps/ui-components/app/components/button-edge/page.tsx new file mode 100644 index 000000000..f65ce5598 --- /dev/null +++ b/apps/ui-components/app/components/button-edge/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/button-edge/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/database-schema-node/page.tsx b/apps/ui-components/app/components/database-schema-node/page.tsx new file mode 100644 index 000000000..60071a5cb --- /dev/null +++ b/apps/ui-components/app/components/database-schema-node/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/database-schema-node/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/devtools/page.tsx b/apps/ui-components/app/components/devtools/page.tsx new file mode 100644 index 000000000..2187e1776 --- /dev/null +++ b/apps/ui-components/app/components/devtools/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/devtools/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/labeled-group-node/page.tsx b/apps/ui-components/app/components/labeled-group-node/page.tsx new file mode 100644 index 000000000..69872e58b --- /dev/null +++ b/apps/ui-components/app/components/labeled-group-node/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/labeled-group-node/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/labeled-handle/page.tsx b/apps/ui-components/app/components/labeled-handle/page.tsx new file mode 100644 index 000000000..d5dd0af2c --- /dev/null +++ b/apps/ui-components/app/components/labeled-handle/page.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { Background, ReactFlow } from "@xyflow/react"; +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/labeled-handle/demo"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: {}, + type: "customNode", + }, +]; + +const nodeTypes = { + customNode: Demo, +}; + +export default function DemoPage() { + return ( + +
+ + + +
+
+ ); +} diff --git a/apps/ui-components/app/components/placeholder-node/page.tsx b/apps/ui-components/app/components/placeholder-node/page.tsx new file mode 100644 index 000000000..a98c9b19b --- /dev/null +++ b/apps/ui-components/app/components/placeholder-node/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/placeholder-node/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/tooltip-node/page.tsx b/apps/ui-components/app/components/tooltip-node/page.tsx new file mode 100644 index 000000000..81cb9c5fe --- /dev/null +++ b/apps/ui-components/app/components/tooltip-node/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/tooltip-node/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/zoom-select/page.tsx b/apps/ui-components/app/components/zoom-select/page.tsx new file mode 100644 index 000000000..0cef537a2 --- /dev/null +++ b/apps/ui-components/app/components/zoom-select/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/zoom-select/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/components/zoom-slider/page.tsx b/apps/ui-components/app/components/zoom-slider/page.tsx new file mode 100644 index 000000000..e373bd0dd --- /dev/null +++ b/apps/ui-components/app/components/zoom-slider/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import ZoomSliderDemo from "@/registry/components/zoom-slider/demo"; + +export default function ZoomSliderDemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/app/globals.css b/apps/ui-components/app/globals.css new file mode 100644 index 000000000..3bebaae74 --- /dev/null +++ b/apps/ui-components/app/globals.css @@ -0,0 +1,79 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } + + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body, + html { + @apply bg-background text-foreground h-full; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/apps/ui-components/app/layout.tsx b/apps/ui-components/app/layout.tsx new file mode 100644 index 000000000..88753de6d --- /dev/null +++ b/apps/ui-components/app/layout.tsx @@ -0,0 +1,26 @@ +import type { Metadata } from "next"; + +import "@xyflow/react/dist/style.css"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "xyflow components registry", + description: + "Here you can find all the components we've built and are available for you to use in your projects.", + robots: { + index: false, + follow: false, + }, +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/apps/ui-components/app/page.tsx b/apps/ui-components/app/page.tsx new file mode 100644 index 000000000..f3c4723ab --- /dev/null +++ b/apps/ui-components/app/page.tsx @@ -0,0 +1,42 @@ +import data from "../public/registry/all-available-components.json"; + +export default async function Home() { + return ( +
+
+

xyflow components registry

+

+ Here you can find all the components we've built and are + available for you to use in your projects. +

+ + +
+ +
+
+

available components

+ +
+
+
+ ); +} diff --git a/apps/ui-components/components.json b/apps/ui-components/components.json new file mode 100644 index 000000000..f87b61877 --- /dev/null +++ b/apps/ui-components/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks", + "registry": "@/registry" + } +} diff --git a/apps/ui-components/components/demo-wrapper.tsx b/apps/ui-components/components/demo-wrapper.tsx new file mode 100644 index 000000000..0d9ac87d3 --- /dev/null +++ b/apps/ui-components/components/demo-wrapper.tsx @@ -0,0 +1,12 @@ +import { PropsWithChildren } from "react"; +import ThemeSwitcher from "./theme-switcher"; +import { ReactFlowProvider } from "@xyflow/react"; + +export default function DemoWrapper(props: PropsWithChildren) { + return ( + + + {props.children} + + ); +} diff --git a/apps/ui-components/components/theme-switcher.tsx b/apps/ui-components/components/theme-switcher.tsx new file mode 100644 index 000000000..1bb2a741e --- /dev/null +++ b/apps/ui-components/components/theme-switcher.tsx @@ -0,0 +1,36 @@ +'use client'; + +import { Panel } from '@xyflow/react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; + +export default function ThemeSwitcher() { + // @todo make this work with system color theme + const onValueChange = (theme: string) => { + const rfElement = document.querySelector('.react-flow'); + + if (rfElement) { + rfElement.classList.toggle('dark', theme === 'dark'); + } + }; + + return ( + + + + ); +} diff --git a/apps/ui-components/components/ui/button.tsx b/apps/ui-components/components/ui/button.tsx new file mode 100644 index 000000000..0270f644a --- /dev/null +++ b/apps/ui-components/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/apps/ui-components/components/ui/select.tsx b/apps/ui-components/components/ui/select.tsx new file mode 100644 index 000000000..ac2a8f2b9 --- /dev/null +++ b/apps/ui-components/components/ui/select.tsx @@ -0,0 +1,164 @@ +"use client" + +import * as React from "react" +import { + CaretSortIcon, + CheckIcon, + ChevronDownIcon, + ChevronUpIcon, +} from "@radix-ui/react-icons" +import * as SelectPrimitive from "@radix-ui/react-select" + +import { cn } from "@/lib/utils" + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +} diff --git a/apps/ui-components/components/ui/slider.tsx b/apps/ui-components/components/ui/slider.tsx new file mode 100644 index 000000000..ab19d576f --- /dev/null +++ b/apps/ui-components/components/ui/slider.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import * as SliderPrimitive from "@radix-ui/react-slider" + +import { cn } from "@/lib/utils" + +const Slider = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)) +Slider.displayName = SliderPrimitive.Root.displayName + +export { Slider } diff --git a/apps/ui-components/components/ui/table.tsx b/apps/ui-components/components/ui/table.tsx new file mode 100644 index 000000000..c0df655c0 --- /dev/null +++ b/apps/ui-components/components/ui/table.tsx @@ -0,0 +1,120 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ + +)) +Table.displayName = "Table" + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableHeader.displayName = "TableHeader" + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableBody.displayName = "TableBody" + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0", + className + )} + {...props} + /> +)) +TableFooter.displayName = "TableFooter" + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableRow.displayName = "TableRow" + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
[role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> +)) +TableHead.displayName = "TableHead" + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + [role=checkbox]]:translate-y-[2px]", + className + )} + {...props} + /> +)) +TableCell.displayName = "TableCell" + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableCaption.displayName = "TableCaption" + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/apps/ui-components/components/ui/toggle-group.tsx b/apps/ui-components/components/ui/toggle-group.tsx new file mode 100644 index 000000000..1c876bbee --- /dev/null +++ b/apps/ui-components/components/ui/toggle-group.tsx @@ -0,0 +1,61 @@ +"use client" + +import * as React from "react" +import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" +import { type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { toggleVariants } from "@/components/ui/toggle" + +const ToggleGroupContext = React.createContext< + VariantProps +>({ + size: "default", + variant: "default", +}) + +const ToggleGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, size, children, ...props }, ref) => ( + + + {children} + + +)) + +ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName + +const ToggleGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, children, variant, size, ...props }, ref) => { + const context = React.useContext(ToggleGroupContext) + + return ( + + {children} + + ) +}) + +ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName + +export { ToggleGroup, ToggleGroupItem } diff --git a/apps/ui-components/components/ui/toggle.tsx b/apps/ui-components/components/ui/toggle.tsx new file mode 100644 index 000000000..c50c2733f --- /dev/null +++ b/apps/ui-components/components/ui/toggle.tsx @@ -0,0 +1,45 @@ +"use client" + +import * as React from "react" +import * as TogglePrimitive from "@radix-ui/react-toggle" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const toggleVariants = cva( + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground", + { + variants: { + variant: { + default: "bg-transparent", + outline: + "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground", + }, + size: { + default: "h-9 px-3", + sm: "h-8 px-2", + lg: "h-10 px-3", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +const Toggle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, size, ...props }, ref) => ( + +)) + +Toggle.displayName = TogglePrimitive.Root.displayName + +export { Toggle, toggleVariants } diff --git a/apps/ui-components/lib/utils.ts b/apps/ui-components/lib/utils.ts new file mode 100644 index 000000000..bd0c391dd --- /dev/null +++ b/apps/ui-components/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/apps/ui-components/middleware.ts b/apps/ui-components/middleware.ts new file mode 100644 index 000000000..94d993e12 --- /dev/null +++ b/apps/ui-components/middleware.ts @@ -0,0 +1,16 @@ +import type { NextRequest } from "next/server"; +import { track } from "@vercel/analytics/server"; + +export async function middleware(request: NextRequest) { + const { pathname } = request.nextUrl; + + if (pathname.startsWith("/registry") && pathname.endsWith(".json")) { + await track("ui-components", { + url: pathname, + }); + } +} + +export const config = { + matcher: "/registry/:path*", +}; diff --git a/apps/ui-components/next.config.js b/apps/ui-components/next.config.js new file mode 100644 index 000000000..4ede429d7 --- /dev/null +++ b/apps/ui-components/next.config.js @@ -0,0 +1,13 @@ +module.exports = { + async redirects() { + return [ + { + // Match all routes without file extention + // that adhere to *-*-*.json pattern + source: "/:slug(\\w*\\-\\w*\\-*\\w*)", + destination: "/registry/:slug.json", + permanent: true, + }, + ]; + }, +}; diff --git a/apps/ui-components/package.json b/apps/ui-components/package.json new file mode 100644 index 000000000..56432a743 --- /dev/null +++ b/apps/ui-components/package.json @@ -0,0 +1,46 @@ +{ + "name": "ui-components", + "version": "0.1.0", + "private": true, + "packageManager": "pnpm@9.11.0", + "scripts": { + "dev": "pnpm run generate-registry && next dev --port 3004", + "build": "pnpm run generate-registry && next build", + "start": "next start", + "lint": "next lint", + "add-component": "node ./scripts/init-new-component.js", + "generate-registry": "node ./scripts/generate-registry.js" + }, + "dependencies": { + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slider": "^1.2.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", + "@vercel/analytics": "^1.3.1", + "@xyflow/react": "^12.3.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "lucide-react": "^0.453.0", + "next": "14.2.15", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "tailwind-merge": "^2.5.4", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@types/node": "^22", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "eslint": "^8.57.1", + "eslint-config-next": "14.2.15", + "postcss": "^8", + "prettier": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.8", + "tailwindcss": "^3.4.14", + "typescript": "^5.5.4" + } +} diff --git a/apps/ui-components/postcss.config.mjs b/apps/ui-components/postcss.config.mjs new file mode 100644 index 000000000..1a69fd2a4 --- /dev/null +++ b/apps/ui-components/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/apps/ui-components/registry/components/animated-svg-edge/demo.tsx b/apps/ui-components/registry/components/animated-svg-edge/demo.tsx new file mode 100644 index 000000000..5a3cc5184 --- /dev/null +++ b/apps/ui-components/registry/components/animated-svg-edge/demo.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { Background, ReactFlow } from "@xyflow/react"; + +import { AnimatedSvgEdge } from "@/registry/components/animated-svg-edge"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { label: "A" }, + }, + { + id: "2", + position: { x: 400, y: 400 }, + data: { label: "B" }, + }, +]; + +const defaultEdges = [ + { + id: "1->2", + source: "1", + target: "2", + type: "animatedSvgEdge", + data: { + duration: 2, + shape: "package", + path: "smoothstep", + }, + } satisfies AnimatedSvgEdge, +]; + +const edgeTypes = { + animatedSvgEdge: AnimatedSvgEdge, +}; + +export default function AnimatedSvgEdgeDemo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/animated-svg-edge/index.tsx b/apps/ui-components/registry/components/animated-svg-edge/index.tsx new file mode 100644 index 000000000..435fd9298 --- /dev/null +++ b/apps/ui-components/registry/components/animated-svg-edge/index.tsx @@ -0,0 +1,247 @@ +import type { Edge, EdgeProps, Position } from "@xyflow/react"; +import { + BaseEdge, + getBezierPath, + getStraightPath, + getSmoothStepPath, +} from "@xyflow/react"; + +export type AnimatedSvgEdge = Edge<{ + /** + * The amount of time it takes, in seconds, to move the shape one from end of + * the edge path to the other. + */ + duration: number; + /** + * The direction in which the shape moves along the edge path. Each value + * corresponds to the following behaviour: + * + * - `forward`: The shape moves from the source node to the target node. + * + * - `reverse`: The shape moves from the target node to the source node. + * + * - `alternate`: The shape moves from the source node to the target node and + * then back to the source node. + * + * - `alternate-reverse`: The shape moves from the target node to the source + * node and then back to the target node. + * + * If not provided, this defaults to `"forward"`. + */ + direction?: "forward" | "reverse" | "alternate" | "alternate-reverse"; + /** + * Which of React Flow's path algorithms to use. Each value corresponds to one + * of React Flow's built-in edge types. + * + * If not provided, this defaults to `"bezier"`. + */ + path?: "bezier" | "smoothstep" | "step" | "straight"; + /** + * The number of times to repeat the animation before stopping. If set to + * `"indefinite"`, the animation will repeat indefinitely. + * + * If not provided, this defaults to `"indefinite"`. + */ + repeat?: number | "indefinite"; + shape: keyof typeof shapes; +}>; + +/** + * The `AnimatedSvgEdge` component renders a typical React Flow edge and animates + * an SVG shape along the edge's path. + */ +export function AnimatedSvgEdge({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + data = { + duration: 2, + direction: "forward", + path: "bezier", + repeat: "indefinite", + shape: "circle", + }, + ...delegated +}: EdgeProps) { + const Shape = shapes[data.shape]; + + const [path] = getPath({ + type: data.path ?? "bezier", + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + const animateMotionProps = getAnimateMotionProps({ + duration: data.duration, + direction: data.direction ?? "forward", + repeat: data.repeat ?? "indefinite", + path, + }); + + return ( + <> + + + + ); +} + +type AnimateMotionProps = { + dur: string; + keyTimes: string; + keyPoints: string; + repeatCount: number | "indefinite"; + path: string; +}; + +type AnimatedSvg = React.FC<{ animateMotionProps: AnimateMotionProps }>; + +const shapes = { + circle: ({ animateMotionProps }) => ( + + + + ), + + package: ({ animateMotionProps }) => ( + + + + + + + + ), +} satisfies Record; + +/** + * Chooses which of React Flow's edge path algorithms to use based on the provided + * `type`. + */ +function getPath({ + type, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, +}: { + type: "bezier" | "smoothstep" | "step" | "straight"; + sourceX: number; + sourceY: number; + targetX: number; + targetY: number; + sourcePosition: Position; + targetPosition: Position; +}) { + switch (type) { + case "bezier": + return getBezierPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + }); + + case "smoothstep": + return getSmoothStepPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + }); + + case "step": + return getSmoothStepPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + borderRadius: 0, + }); + + case "straight": + return getStraightPath({ + sourceX, + sourceY, + targetX, + targetY, + }); + } +} + +/** + * Construct the props for an `` element based on an + * `AnimatedSvgEdge`'s data. + */ +function getAnimateMotionProps({ + duration, + direction, + repeat, + path, +}: { + duration: number; + direction: "forward" | "reverse" | "alternate" | "alternate-reverse"; + repeat: number | "indefinite"; + path: string; +}) { + const base = { + path, + repeatCount: repeat, + // The default calcMode for the `` element is "paced", which + // is not compatible with the `keyPoints` attribute. Setting this to "linear" + // ensures that the shape correc follows the path. + calcMode: "linear", + }; + + switch (direction) { + case "forward": + return { + ...base, + dur: `${duration}s`, + keyTimes: "0;1", + keyPoints: "0;1", + }; + + case "reverse": + return { + ...base, + dur: `${duration}s`, + keyTimes: "0;1", + keyPoints: "1;0", + }; + + case "alternate": + return { + ...base, + // By doubling the animation duration, the time spent moving from one end + // to the other remains consistent when switching between directions. + dur: `${duration * 2}s`, + keyTimes: "0;0.5;1", + keyPoints: "0;1;0", + }; + + case "alternate-reverse": + return { + ...base, + dur: `${duration * 2}s`, + keyTimes: "0;0.5;1", + keyPoints: "1;0;1", + }; + } +} diff --git a/apps/ui-components/registry/components/animated-svg-edge/registry.json b/apps/ui-components/registry/components/animated-svg-edge/registry.json new file mode 100644 index 000000000..822648041 --- /dev/null +++ b/apps/ui-components/registry/components/animated-svg-edge/registry.json @@ -0,0 +1,16 @@ +{ + "name": "animated-svg-edge", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": [], + "files": [ + { + "type": "registry:component", + "path": "animated-svg-edge.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/annotation-node/demo.tsx b/apps/ui-components/registry/components/annotation-node/demo.tsx new file mode 100644 index 000000000..051844c08 --- /dev/null +++ b/apps/ui-components/registry/components/annotation-node/demo.tsx @@ -0,0 +1,79 @@ +"use client"; + +import { Background, ReactFlow } from "@xyflow/react"; +import { AnnotationNode } from "@/registry/components/annotation-node"; + +const nodeTypes = { + annotationNode: AnnotationNode, +}; + +const defaultNodes = [ + { + id: "1a", + type: "input", + data: { label: "Node 1" }, + position: { x: 0, y: 0 }, + }, + { + id: "1b", + position: { x: -150, y: -50 }, + parentId: "1a", + data: { + level: 1, + label: "Annotate your flows any way you like.", + arrowStyle: { + right: 30, + bottom: 0, + transform: "rotate(-60deg)", + }, + arrow: "⤹", + }, + type: "annotationNode", + }, + { + id: "2a", + data: { label: "Node 2" }, + position: { x: -100, y: 120 }, + }, + { + id: "3a", + data: { label: "Node 3" }, + position: { x: 100, y: 120 }, + }, + { + id: "3b", + position: { x: 100, y: -110 }, + parentId: "3a", + data: { + level: 2, + label: "Connect annotations to nodes to adjust interactively.", + arrowStyle: { + left: 20, + bottom: -25, + transform: "rotate(-10deg) ", + }, + arrow: "⤹", + }, + type: "annotationNode", + }, +]; + +const defaultEdges = [ + { id: "e1-2", source: "1a", target: "2a" }, + { id: "e1-3", source: "1a", target: "3a" }, +]; + +export default function AnnotationNodeDemo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/annotation-node/index.tsx b/apps/ui-components/registry/components/annotation-node/index.tsx new file mode 100644 index 000000000..3673d7e2a --- /dev/null +++ b/apps/ui-components/registry/components/annotation-node/index.tsx @@ -0,0 +1,32 @@ +import { type Node, type NodeProps } from "@xyflow/react"; +import { CSSProperties } from "react"; + +type AnnotationNode = Node<{ + label: string; + level: number; + arrow: string; + arrowStyle: CSSProperties; +}>; + +export function AnnotationNode({ data }: NodeProps) { + return ( +
+
{data.level}.
+
{data.label}
+ {data.arrowStyle && ( +
+ {data.arrow} +
+ )} +
+ ); +} + +AnnotationNode.displayName = "AnnotationNode"; diff --git a/apps/ui-components/registry/components/annotation-node/registry.json b/apps/ui-components/registry/components/annotation-node/registry.json new file mode 100644 index 000000000..edc6ee560 --- /dev/null +++ b/apps/ui-components/registry/components/annotation-node/registry.json @@ -0,0 +1,16 @@ +{ + "name": "annotation-node", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": [], + "files": [ + { + "type": "registry:component", + "path": "annotation-node.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/base-handle/demo.tsx b/apps/ui-components/registry/components/base-handle/demo.tsx new file mode 100644 index 000000000..4712f6003 --- /dev/null +++ b/apps/ui-components/registry/components/base-handle/demo.tsx @@ -0,0 +1,20 @@ +import React, { memo } from "react"; +import { NodeProps, Position } from "@xyflow/react"; + +import { BaseHandle } from "@/registry/components/base-handle"; +import { BaseNode } from "@/registry/components/base-node"; + +// You can use Handle components only inside of custom nodes. +const CustomNode = memo(({ selected }: NodeProps) => { + return ( + + +
A node with two handles
+ +
+ ); +}); + +CustomNode.displayName = "BaseHandleDemo"; + +export default CustomNode; diff --git a/apps/ui-components/registry/components/base-handle/index.tsx b/apps/ui-components/registry/components/base-handle/index.tsx new file mode 100644 index 000000000..34807e51d --- /dev/null +++ b/apps/ui-components/registry/components/base-handle/index.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import { Handle, HandleProps } from "@xyflow/react"; +import { cn } from "@/lib/utils"; + +export const BaseHandle = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & HandleProps +>(({ className, ...props }, ref) => ( + +)); +BaseHandle.displayName = "BaseHandle"; diff --git a/apps/ui-components/registry/components/base-handle/registry.json b/apps/ui-components/registry/components/base-handle/registry.json new file mode 100644 index 000000000..9cf00056f --- /dev/null +++ b/apps/ui-components/registry/components/base-handle/registry.json @@ -0,0 +1,16 @@ +{ + "name": "base-handle", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": [], + "files": [ + { + "type": "registry:component", + "path": "base-handle.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/base-node/demo.tsx b/apps/ui-components/registry/components/base-node/demo.tsx new file mode 100644 index 000000000..a17b56d56 --- /dev/null +++ b/apps/ui-components/registry/components/base-node/demo.tsx @@ -0,0 +1,47 @@ +"use client"; + +import { + Background, + Handle, + NodeProps, + Position, + ReactFlow, +} from "@xyflow/react"; +import { BaseNode } from "@/registry/components/base-node"; + +const defaultNodes = [ + { + id: "1", + position: { x: 0, y: 0 }, + type: "customNode", + data: { + label: "Custom Node", + }, + }, +]; + +const nodeTypes = { + customNode: CustomNode, +}; + +function CustomNode({ data }: NodeProps) { + return ( + + <> + {data.label} + + + + + ); +} + +export default function Demo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/base-node/index.tsx b/apps/ui-components/registry/components/base-node/index.tsx new file mode 100644 index 000000000..ac0848392 --- /dev/null +++ b/apps/ui-components/registry/components/base-node/index.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { cn } from "@/lib/utils"; + +export const BaseNode = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & { selected?: boolean } +>(({ className, selected, ...props }, ref) => ( +
+)); +BaseNode.displayName = "BaseNode"; diff --git a/apps/ui-components/registry/components/base-node/registry.json b/apps/ui-components/registry/components/base-node/registry.json new file mode 100644 index 000000000..23dc46d98 --- /dev/null +++ b/apps/ui-components/registry/components/base-node/registry.json @@ -0,0 +1,16 @@ +{ + "name": "base-node", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": [], + "files": [ + { + "type": "registry:component", + "path": "base-node.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/button-edge/demo.tsx b/apps/ui-components/registry/components/button-edge/demo.tsx new file mode 100644 index 000000000..1ed2ced1e --- /dev/null +++ b/apps/ui-components/registry/components/button-edge/demo.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { Background, ReactFlow } from "@xyflow/react"; +import { ButtonEdge } from "@/registry/components/button-edge"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { label: "Node" }, + }, + { + id: "2", + position: { x: 500, y: 500 }, + data: { label: "Node" }, + }, +]; + +const defaultEdges = [ + { + id: "e1-2", + source: "1", + target: "2", + type: "buttonEdge", + }, +]; + +const edgeTypes = { + buttonEdge: ButtonEdge, +}; + +export default function Demo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/button-edge/index.tsx b/apps/ui-components/registry/components/button-edge/index.tsx new file mode 100644 index 000000000..d54a7cd19 --- /dev/null +++ b/apps/ui-components/registry/components/button-edge/index.tsx @@ -0,0 +1,53 @@ +import { + BaseEdge, + EdgeLabelRenderer, + EdgeProps, + getBezierPath, + useReactFlow, +} from "@xyflow/react"; + +import { Button } from "@/components/ui/button"; +import { MousePointerClick } from "lucide-react"; + +export function ButtonEdge({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + style = {}, + markerEnd, +}: EdgeProps) { + const [edgePath, labelX, labelY] = getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + const onEdgeClick = () => { + window.alert(`Edge with id: ${id} has been clicked!`); + }; + + return ( + <> + + +
+ +
+
+ + ); +} diff --git a/apps/ui-components/registry/components/button-edge/registry.json b/apps/ui-components/registry/components/button-edge/registry.json new file mode 100644 index 000000000..a3d42591e --- /dev/null +++ b/apps/ui-components/registry/components/button-edge/registry.json @@ -0,0 +1,16 @@ +{ + "name": "button-edge", + "type": "registry:component", + "dependencies": ["@xyflow/react", "lucide-react"], + "devDependencies": [], + "registryDependencies": ["button"], + "files": [ + { + "type": "registry:component", + "path": "button-edge.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/database-schema-node/demo.tsx b/apps/ui-components/registry/components/database-schema-node/demo.tsx new file mode 100644 index 000000000..53c18f537 --- /dev/null +++ b/apps/ui-components/registry/components/database-schema-node/demo.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { Background, Edge, ReactFlow } from "@xyflow/react"; +import { DatabaseSchemaNode } from "@/registry/components/database-schema-node"; + +const defaultNodes = [ + { + id: "1", + position: { x: 0, y: 0 }, + type: "databaseSchema", + data: { + label: "Products", + schema: [ + { title: "id", type: "uuid" }, + { title: "name", type: "varchar" }, + { title: "description", type: "varchar" }, + { title: "warehouse_id", type: "uuid" }, + { title: "supplier_id", type: "uuid" }, + { title: "price", type: "money" }, + { title: "quantity", type: "int4" }, + ], + }, + }, + { + id: "2", + position: { x: 350, y: -100 }, + type: "databaseSchema", + data: { + label: "Warehouses", + schema: [ + { title: "id", type: "uuid" }, + { title: "name", type: "varchar" }, + { title: "address", type: "varchar" }, + { title: "capacity", type: "int4" }, + ], + }, + }, + { + id: "3", + position: { x: 350, y: 200 }, + type: "databaseSchema", + data: { + label: "Suppliers", + schema: [ + { title: "id", type: "uuid" }, + { title: "name", type: "varchar" }, + { title: "description", type: "varchar" }, + { title: "country", type: "varchar" }, + ], + }, + }, +]; + +const defaultEdges: Edge[] = [ + { + id: "products-warehouses", + source: "1", + target: "2", + sourceHandle: "warehouse_id", + targetHandle: "id", + }, + { + id: "products-suppliers", + source: "1", + target: "3", + sourceHandle: "supplier_id", + targetHandle: "id", + }, +]; + +const nodeTypes = { + databaseSchema: DatabaseSchemaNode, +}; + +export default function Demo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/database-schema-node/index.tsx b/apps/ui-components/registry/components/database-schema-node/index.tsx new file mode 100644 index 000000000..37529f4a3 --- /dev/null +++ b/apps/ui-components/registry/components/database-schema-node/index.tsx @@ -0,0 +1,52 @@ +import { Node, NodeProps, Position } from "@xyflow/react"; + +import { TableBody, TableCell, TableRow } from "@/components/ui/table"; + +import { BaseNode } from "@/registry/components/base-node"; +import { LabeledHandle } from "@/registry/components/labeled-handle"; + +type DatabaseSchemaNode = Node<{ + label: string; + schema: { title: string; type: string }[]; +}>; + +export function DatabaseSchemaNode({ + data, + selected, +}: NodeProps) { + return ( + +

+ {data.label} +

+ {/* shadcn Table cannot be used because of hardcoded overflow-auto */} + + + {data.schema.map((entry) => ( + + + + + + + + + ))} + +
+
+ ); +} diff --git a/apps/ui-components/registry/components/database-schema-node/registry.json b/apps/ui-components/registry/components/database-schema-node/registry.json new file mode 100644 index 000000000..62534ff0d --- /dev/null +++ b/apps/ui-components/registry/components/database-schema-node/registry.json @@ -0,0 +1,20 @@ +{ + "name": "database-schema-node", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": [ + "table", + "https://ui.reactflow.dev/base-node", + "https://ui.reactflow.dev/labeled-handle" + ], + "files": [ + { + "type": "registry:component", + "path": "database-schema-node.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/devtools/demo.tsx b/apps/ui-components/registry/components/devtools/demo.tsx new file mode 100644 index 000000000..fd52a515d --- /dev/null +++ b/apps/ui-components/registry/components/devtools/demo.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { Background, ReactFlow } from "@xyflow/react"; + +import { DevTools } from "@/registry/components/devtools"; + +const defaultNodes = [ + { + id: '1a', + type: 'input', + data: { label: 'Node 1' }, + position: { x: 250, y: 5 }, + }, + { + id: '2a', + data: { label: 'Node 2' }, + position: { x: 100, y: 120 }, + }, + { + id: '3a', + data: { label: 'Node 3' }, + position: { x: 400, y: 120 }, + }, +]; + +const defaultEdges = [ + { id: 'e1-2', source: '1a', target: '2a' }, + { id: 'e1-3', source: '1a', target: '3a' }, +]; + +export default function DevtoolsDemo() { + return ( +
+ + + + +
+ ); +} diff --git a/apps/ui-components/registry/components/devtools/index.tsx b/apps/ui-components/registry/components/devtools/index.tsx new file mode 100644 index 000000000..69aa05a69 --- /dev/null +++ b/apps/ui-components/registry/components/devtools/index.tsx @@ -0,0 +1,244 @@ +import { + useEffect, + useState, + type Dispatch, + type SetStateAction, + useCallback +} from 'react'; + +import { + useNodes, + Panel, + useStore, + useStoreApi, + type OnNodesChange, + type NodeChange, + type XYPosition, + ViewportPortal, + useReactFlow +} from '@xyflow/react'; + +import { + ToggleGroup, + ToggleGroupItem, +} from "@/components/ui/toggle-group" + +export function ViewportLogger() { + const viewport = useStore( + (s) => + `x: ${s.transform[0].toFixed(2)}, y: ${s.transform[1].toFixed(2)}, zoom: ${s.transform[2].toFixed(2)}`, + ); + + return
{viewport}
; +} + +type ChangeLoggerProps = { + color?: string; + limit?: number; +}; + +type ChangeInfoProps = { + change: NodeChange; +}; + +function ChangeInfo({ change }: ChangeInfoProps) { + const id = 'id' in change ? change.id : '-'; + const { type } = change; + + return ( +
+
node id: {id}
+
+ {type === 'add' ? JSON.stringify(change.item, null, 2) : null} + {type === 'dimensions' + ? `dimensions: ${change.dimensions?.width} × ${change.dimensions?.height}` + : null} + {type === 'position' + ? `position: ${change.position?.x.toFixed(1)}, ${change.position?.y.toFixed(1)}` + : null} + {type === 'remove' ? 'remove' : null} + {type === 'select' ? (change.selected ? 'select' : 'unselect') : null} +
+
+ ); +} + +export function ChangeLogger({ limit = 20 }: ChangeLoggerProps) { + const [changes, setChanges] = useState([]); + const store = useStoreApi(); + + // Memoize the callback for handling node changes + const handleNodeChanges: OnNodesChange = useCallback( + (newChanges: NodeChange[]) => { + setChanges((prevChanges) => [...newChanges, ...prevChanges].slice(0, limit)); + }, + [limit] + ); + + useEffect(() => { + store.setState({ onNodesChange: handleNodeChanges }); + + return () => store.setState({ onNodesChange: undefined }); + }, [handleNodeChanges, store]); + + const NoChanges = () =>
No Changes Triggered
; + + return ( + <> + {changes.length === 0 ? ( + + ) : ( + changes.map((change, index) => ) + )} + + ); +} + +export function NodeInspector() { + const { getInternalNode } = useReactFlow(); + const nodes = useNodes(); + + return ( + +
+ {nodes.map((node) => { + const internalNode = getInternalNode(node.id); + if (!internalNode) { + return null; + } + + const absPosition = internalNode?.internals.positionAbsolute; + + return ( + + ); + })} +
+
+ ); +} + +type NodeInfoProps = { + id: string; + type: string; + selected: boolean; + position: XYPosition; + absPosition: XYPosition; + width?: number; + height?: number; + data: any; +}; + +function NodeInfo({ + id, + type, + selected, + position, + absPosition, + width, + height, + data, +}: NodeInfoProps) { + if (!width || !height) return null; + + const absoluteTransform = `translate(${absPosition.x}px, ${absPosition.y + height}px)`; + const formattedPosition = `${position.x.toFixed(1)}, ${position.y.toFixed(1)}`; + const formattedDimensions = `${width} × ${height}`; + const selectionStatus = selected ? 'Selected' : 'Not Selected'; + + return ( +
+
id: {id}
+
type: {type}
+
selected: {selectionStatus}
+
position: {formattedPosition}
+
dimensions: {formattedDimensions}
+
data: {JSON.stringify(data, null, 2)}
+
+ ); +} + + +type Tool = { + active: boolean; + setActive: Dispatch>; + label: string; + value: string; +}; + +type DevToolsToggleProps = { + tools: Tool[]; +}; + +function DevToolsToggle({ tools }: DevToolsToggleProps) { + return ( + + + {tools.map(({ active, setActive, label, value }) => ( + setActive((prev) => !prev)} + aria-pressed={active} + className="bg-card text-card-foreground transition-colors duration-300 hover:bg-secondary hover:text-secondary-foreground" + > + {label} + + ))} + + + ); +} + + +export function DevTools() { + const [nodeInspectorActive, setNodeInspectorActive] = useState(false); + const [changeLoggerActive, setChangeLoggerActive] = useState(false); + const [viewportLoggerActive, setViewportLoggerActive] = useState(false); + + + const tools = [ + { active: nodeInspectorActive, setActive: setNodeInspectorActive, label: 'Node Inspector', value: 'node-inspector' }, + { active: changeLoggerActive, setActive: setChangeLoggerActive, label: 'Change Logger', value: 'change-logger' }, + { active: viewportLoggerActive, setActive: setViewportLoggerActive, label: 'Viewport Logger', value: 'viewport-logger' }, + ]; + + return ( + <> + + + {changeLoggerActive && ( + + + + )} + + {nodeInspectorActive && } + + {viewportLoggerActive && ( + + + + )} + + ); +} + +DevTools.displayName = "DevTools"; \ No newline at end of file diff --git a/apps/ui-components/registry/components/devtools/registry.json b/apps/ui-components/registry/components/devtools/registry.json new file mode 100644 index 000000000..547fae523 --- /dev/null +++ b/apps/ui-components/registry/components/devtools/registry.json @@ -0,0 +1,16 @@ +{ + "name": "devtools", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": ["toggle-group"], + "files": [ + { + "type": "registry:component", + "path": "devtools.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/labeled-group-node/demo.tsx b/apps/ui-components/registry/components/labeled-group-node/demo.tsx new file mode 100644 index 000000000..c004e2d55 --- /dev/null +++ b/apps/ui-components/registry/components/labeled-group-node/demo.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { Background, ReactFlow, Node } from "@xyflow/react"; +import { LabeledGroupNode } from "@/registry/components/labeled-group-node"; + +const nodeTypes = { + labeledGroupNode: LabeledGroupNode, +}; + + +const defaultNodes: Node[] = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { label: "Group Node" }, + width: 380, + height: 200, + type: "labeledGroupNode" + }, + { + id: "2", + position: { x: 50, y: 100 }, + data: { label: "Node" }, + type: "default", + parentId: "1", + extent: "parent" + }, + { + id: "3", + position: { x: 200, y: 50 }, + data: { label: "Node" }, + type: "default", + parentId: "1", + extent: "parent" + }, +]; + +export default function LabeledGroupNodeDemo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/labeled-group-node/index.tsx b/apps/ui-components/registry/components/labeled-group-node/index.tsx new file mode 100644 index 000000000..8a7601ce7 --- /dev/null +++ b/apps/ui-components/registry/components/labeled-group-node/index.tsx @@ -0,0 +1,23 @@ +import { Node, NodeProps } from "@xyflow/react"; +import { BaseNode } from "@/registry/components/base-node"; + +type LabeledGroupNode = Node<{ + label: string; +}>; + +export function LabeledGroupNode({ data, selected }: NodeProps) { + return ( + + {data.label && ( +
+ {data.label} +
+ )} +
+ ); +} + +LabeledGroupNode.displayName = "LabeledGroupNode"; diff --git a/apps/ui-components/registry/components/labeled-group-node/registry.json b/apps/ui-components/registry/components/labeled-group-node/registry.json new file mode 100644 index 000000000..9dd7f1afb --- /dev/null +++ b/apps/ui-components/registry/components/labeled-group-node/registry.json @@ -0,0 +1,16 @@ +{ + "name": "labeled-group-node", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": ["https://ui.reactflow.dev/base-node"], + "files": [ + { + "type": "registry:component", + "path": "labeled-group-node.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/labeled-handle/demo.tsx b/apps/ui-components/registry/components/labeled-handle/demo.tsx new file mode 100644 index 000000000..8b88cfe85 --- /dev/null +++ b/apps/ui-components/registry/components/labeled-handle/demo.tsx @@ -0,0 +1,57 @@ +import React, { memo } from "react"; +import { NodeProps, Position } from "@xyflow/react"; + +import { LabeledHandle } from "@/registry/components/labeled-handle"; +import { BaseNode } from "@/registry/components/base-node"; + +// You can use Handle components only inside of custom nodes. +const CustomNode = memo(({ selected }: NodeProps) => { + return ( + +
+ + + +
+
+ + + +
+
+ ); +}); + +CustomNode.displayName = "LabeledHandleDemo"; + +export default CustomNode; diff --git a/apps/ui-components/registry/components/labeled-handle/index.tsx b/apps/ui-components/registry/components/labeled-handle/index.tsx new file mode 100644 index 000000000..15066a0fa --- /dev/null +++ b/apps/ui-components/registry/components/labeled-handle/index.tsx @@ -0,0 +1,45 @@ +"use client"; +import React from "react"; +import { cn } from "@/lib/utils"; +import { HandleProps } from "@xyflow/react"; + +import { BaseHandle } from "@/registry/components/base-handle"; + +function getFlexDirection(position: string) { + let flexDirection = + position === "top" || position === "bottom" ? "flex-col" : "flex-row"; + switch (position) { + case "bottom": + case "right": + return flexDirection + "-reverse justify-end"; + default: + return flexDirection; + } +} + +const LabeledHandle = React.forwardRef< + HTMLDivElement, + HandleProps & + React.HTMLAttributes & { + title: string; + handleClassName?: string; + labelClassName?: string; + } +>(({ className, labelClassName, title, position, ...props }, ref) => ( +
+ + +
+)); + +LabeledHandle.displayName = "LabeledHandle"; + +export { LabeledHandle }; diff --git a/apps/ui-components/registry/components/labeled-handle/registry.json b/apps/ui-components/registry/components/labeled-handle/registry.json new file mode 100644 index 000000000..927dfda7f --- /dev/null +++ b/apps/ui-components/registry/components/labeled-handle/registry.json @@ -0,0 +1,16 @@ +{ + "name": "labeled-handle", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": ["https://ui.reactflow.dev/base-handle"], + "files": [ + { + "type": "registry:component", + "path": "labeled-handle.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/placeholder-node/demo.tsx b/apps/ui-components/registry/components/placeholder-node/demo.tsx new file mode 100644 index 000000000..a7e124dc7 --- /dev/null +++ b/apps/ui-components/registry/components/placeholder-node/demo.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { + Background, + ReactFlow, +} from "@xyflow/react"; +import { PlaceholderNode } from "@/registry/components/placeholder-node"; + +const nodeTypes = { + placeholder: PlaceholderNode, +}; + + +const defaultNodes = [ + { + id: '1', + data: { label: 'Original Node' }, + position: { x: 0, y: 0 }, + type: 'default', + }, + { + id: '2', + data: { label: '+' }, + position: { x: 0, y: 150 }, + type: 'placeholder', + }, +]; + +const defaultEdges = [ + { + id: '1=>2', + source: '1', + target: '2', + type: 'default', + animated: true, + }, +]; + +export default function Demo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/placeholder-node/index.tsx b/apps/ui-components/registry/components/placeholder-node/index.tsx new file mode 100644 index 000000000..b5700c0a1 --- /dev/null +++ b/apps/ui-components/registry/components/placeholder-node/index.tsx @@ -0,0 +1,59 @@ +import React from "react"; +import { useReactFlow, Handle, Position, NodeProps, Node } from "@xyflow/react"; +import { BaseNode } from "@/registry/components/base-node"; + +type PlaceholderNodeData = Node<{ + label: string; +}>; + +export function PlaceholderNode({ data, id, selected }: NodeProps) { + + const { setNodes, setEdges } = useReactFlow(); + + const handleClick = () => { + setEdges((edges) => + edges.map((edge) => + edge.target === id ? { ...edge, animated: false } : edge + ) + ); + + setNodes((nodes) => { + const updatedNodes = nodes.map((node) => { + if (node.id === id) { + return { + ...node, + data: { ...node.data, label: "New Node" }, + type: "default", + }; + } + return node; + }); + return updatedNodes; + }); + }; + + return ( + + {data.label} + + + + ); +} + +PlaceholderNode.displayName = "PlaceholderNode"; diff --git a/apps/ui-components/registry/components/placeholder-node/registry.json b/apps/ui-components/registry/components/placeholder-node/registry.json new file mode 100644 index 000000000..2f071d40b --- /dev/null +++ b/apps/ui-components/registry/components/placeholder-node/registry.json @@ -0,0 +1,16 @@ +{ + "name": "placeholder-node", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": ["https://ui.reactflow.dev/base-node"], + "files": [ + { + "type": "registry:component", + "path": "placeholder-node.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/tooltip-node/demo.tsx b/apps/ui-components/registry/components/tooltip-node/demo.tsx new file mode 100644 index 000000000..06597e6ac --- /dev/null +++ b/apps/ui-components/registry/components/tooltip-node/demo.tsx @@ -0,0 +1,70 @@ +"use client"; + +import { Background, Position, ReactFlow } from "@xyflow/react"; +import { + TooltipNode, + type TooltipNodeType, +} from "@/registry/components/tooltip-node"; + +const nodeTypes = { + tooltip: TooltipNode, +}; + +const defaultNodes: TooltipNodeType[] = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { + label: "Node with bottom tooltip", + tooltip: { + label: "I am a tooltip", + position: Position.Bottom, + }, + }, + type: "tooltip", + }, + { + id: "2", + position: { x: 350, y: 380 }, + data: { + label: "Node with top tooltip", + tooltip: { + label: "I am a tooltip", + position: Position.Top, + }, + }, + type: "tooltip", + }, + { + id: "3", + position: { x: 0, y: 320 }, + data: { + label: "Node with right tooltip", + tooltip: { + label: "I am a tooltip", + position: Position.Right, + }, + }, + type: "tooltip", + }, +]; + +const defaultEdges = [ + { id: "e1-2", source: "1", target: "2" }, + { id: "e1-3", source: "1", target: "3" }, +]; + +export default function TooltipNodeDemo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/registry/components/tooltip-node/index.tsx b/apps/ui-components/registry/components/tooltip-node/index.tsx new file mode 100644 index 000000000..80965c559 --- /dev/null +++ b/apps/ui-components/registry/components/tooltip-node/index.tsx @@ -0,0 +1,41 @@ +import { useState } from "react"; +import { + Node, + NodeProps, + NodeToolbar, + NodeToolbarProps, + Handle, + Position, +} from "@xyflow/react"; +import { BaseNode } from "@/registry/components/base-node"; + +export type TooltipNodeType = Node<{ + label: string; + tooltip?: { + label: string; + position?: NodeToolbarProps["position"]; + }; +}>; + +export function TooltipNode({ data, selected }: NodeProps) { + const [isTooltipVisible, setTooltipVisible] = useState(false); + + return ( + setTooltipVisible(true)} + onMouseLeave={() => setTooltipVisible(false)} + selected={selected} + > + + {data.tooltip?.label} + +
{data.label}
+ + +
+ ); +} diff --git a/apps/ui-components/registry/components/tooltip-node/registry.json b/apps/ui-components/registry/components/tooltip-node/registry.json new file mode 100644 index 000000000..8782cc8f7 --- /dev/null +++ b/apps/ui-components/registry/components/tooltip-node/registry.json @@ -0,0 +1,16 @@ +{ + "name": "tooltip-node", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": ["https://ui.reactflow.dev/base-node"], + "files": [ + { + "type": "registry:component", + "path": "tooltip-node.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/zoom-select/demo.tsx b/apps/ui-components/registry/components/zoom-select/demo.tsx new file mode 100644 index 000000000..17c56068d --- /dev/null +++ b/apps/ui-components/registry/components/zoom-select/demo.tsx @@ -0,0 +1,21 @@ +import { Background, ReactFlow } from "@xyflow/react"; +import { ZoomSelect } from "@/registry/components/zoom-select"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { label: "Node" }, + }, +]; + +export default function ZoomSelectDemo() { + return ( +
+ + + + +
+ ); +} diff --git a/apps/ui-components/registry/components/zoom-select/index.tsx b/apps/ui-components/registry/components/zoom-select/index.tsx new file mode 100644 index 000000000..b06e9ebdf --- /dev/null +++ b/apps/ui-components/registry/components/zoom-select/index.tsx @@ -0,0 +1,76 @@ +"use client"; + +import * as React from "react"; +import { + Panel, + useReactFlow, + PanelProps, + useStore, +} from "@xyflow/react"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { cn } from "@/lib/utils"; + +const ZoomSelect = React.forwardRef< + HTMLDivElement, + Omit +>(({ className, ...props }) => { + const { zoomTo, fitView } = useReactFlow(); + + const handleZoomChange = (value: string) => { + if (value === "best-fit") { + fitView(); + } else { + const zoomValue = parseFloat(value); + if (!isNaN(zoomValue)) { + zoomTo(zoomValue); + } + } + }; + + const { zoomLevels } = useStore((state) => { + const minZoom = state.minZoom; + const maxZoom = state.maxZoom; + + const levels = []; + const zoomIncrement = 50; + for (let i = Math.ceil(minZoom * 100); i <= Math.floor(maxZoom * 100); i += zoomIncrement) { + levels.push((i / 100).toString()); + } + + return { zoomLevels: levels }; + }); + + + return ( + + + + ); +}); + +ZoomSelect.displayName = "ZoomSelect"; + +export { ZoomSelect }; diff --git a/apps/ui-components/registry/components/zoom-select/registry.json b/apps/ui-components/registry/components/zoom-select/registry.json new file mode 100644 index 000000000..44a346722 --- /dev/null +++ b/apps/ui-components/registry/components/zoom-select/registry.json @@ -0,0 +1,16 @@ +{ + "name": "zoom-select", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": ["select"], + "files": [ + { + "type": "registry:component", + "path": "zoom-select.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/registry/components/zoom-slider/demo.tsx b/apps/ui-components/registry/components/zoom-slider/demo.tsx new file mode 100644 index 000000000..e50d4a027 --- /dev/null +++ b/apps/ui-components/registry/components/zoom-slider/demo.tsx @@ -0,0 +1,21 @@ +import { Background, ReactFlow } from "@xyflow/react"; +import { ZoomSlider } from "@/registry/components/zoom-slider"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { label: "Node" }, + }, +]; + +export default function ZoomSliderDemo() { + return ( +
+ + + + +
+ ); +} diff --git a/apps/ui-components/registry/components/zoom-slider/index.tsx b/apps/ui-components/registry/components/zoom-slider/index.tsx new file mode 100644 index 000000000..3c7d20f9e --- /dev/null +++ b/apps/ui-components/registry/components/zoom-slider/index.tsx @@ -0,0 +1,80 @@ +"use client"; + +import * as React from "react"; +import { Maximize, Minus, Plus } from "lucide-react"; + +import { + Panel, + useViewport, + useStore, + useReactFlow, + PanelProps, +} from "@xyflow/react"; + +import { Slider } from "@/components/ui/slider"; +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; + +const ZoomSlider = React.forwardRef< + HTMLDivElement, + Omit +>(({ className, ...props }) => { + const { zoom } = useViewport(); + const { zoomTo, zoomIn, zoomOut, fitView } = useReactFlow(); + + const { minZoom, maxZoom } = useStore( + (state) => ({ + minZoom: state.minZoom, + maxZoom: state.maxZoom, + }), + (a, b) => a.minZoom !== b.minZoom || a.maxZoom !== b.maxZoom, + ); + + return ( + + + zoomTo(values[0])} + /> + + + + + ); +}); + +ZoomSlider.displayName = "ZoomSlider"; + +export { ZoomSlider }; diff --git a/apps/ui-components/registry/components/zoom-slider/registry.json b/apps/ui-components/registry/components/zoom-slider/registry.json new file mode 100644 index 000000000..7469e3d77 --- /dev/null +++ b/apps/ui-components/registry/components/zoom-slider/registry.json @@ -0,0 +1,16 @@ +{ + "name": "zoom-slider", + "type": "registry:component", + "dependencies": ["@xyflow/react", "lucide-react"], + "devDependencies": [], + "registryDependencies": ["button", "slider"], + "files": [ + { + "type": "registry:component", + "path": "zoom-slider.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/scripts/generate-registry.js b/apps/ui-components/scripts/generate-registry.js new file mode 100644 index 000000000..c989ea920 --- /dev/null +++ b/apps/ui-components/scripts/generate-registry.js @@ -0,0 +1,84 @@ +const fs = require("fs"); +const path = require("path"); + +const componentsPath = path.join(__dirname, "../registry/components/"); +const registryOutputPath = path.join(__dirname, "../public/registry"); +const demoOutputPath = path.join(__dirname, "../public/demo"); + +(async () => { + console.log("Generating registry files..."); + + // Remove old registry files + try { + fs.rmSync(registryOutputPath, { recursive: true, force: true }); + } catch (error) { + console.log("No old registry files found."); + } + + // Remove old demo files + try { + fs.rmSync(demoOutputPath, { recursive: true, force: true }); + } catch (error) { + console.log("No old registry files found."); + } + + // Create the output directories if they don't exist + fs.mkdirSync(registryOutputPath, { recursive: true }); + fs.mkdirSync(demoOutputPath, { recursive: true }); + + // Read the components directory + fs.readdir(componentsPath, { withFileTypes: true }, (_, folders) => { + const all = { components: [] }; + for (const folder of folders) { + // Gather all component names + all.components.push(folder.name); + + // Gather relevant file paths + const componentPath = path.join(componentsPath, folder.name); + const indexPath = path.join(componentPath, "index.tsx"); + const registryPath = path.join(componentPath, "registry.json"); + const demoPath = path.join(componentPath, "demo.tsx"); + + // Read registry file and convert it to an object + const registryRaw = fs.readFileSync(registryPath, "utf8"); + const registry = JSON.parse(registryRaw); + + // Read index file and add it to the registry object + const index = fs.readFileSync(indexPath, "utf8"); + registry.files[0].content = index; + + // Write the registry object into the build folder + const componentOutputPath = path.join( + registryOutputPath, + folder.name + ".json", + ); + fs.writeFileSync(componentOutputPath, JSON.stringify(registry, null, 2)); + + // Read demo.tsx file and add it to the demo object + // TODO: support multiple demo files + const demoTsx = fs.readFileSync(demoPath, "utf8"); + const demoClean = demoTsx.replace(/\/registry\//g, ""); + const demoFile = { + files: [ + { + content: demoClean, + }, + ], + }; + + // Write demo.json + fs.writeFileSync( + path.join(demoOutputPath, folder.name + ".json"), + JSON.stringify(demoFile), + ); + } + + // Write overview registry file + fs.writeFileSync( + registryOutputPath + "/all-available-components.json", + JSON.stringify(all), + ); + }); + + console.log("Registry files successfully generated!"); +})(); diff --git a/apps/ui-components/scripts/init-new-component.js b/apps/ui-components/scripts/init-new-component.js new file mode 100644 index 000000000..eac3493c0 --- /dev/null +++ b/apps/ui-components/scripts/init-new-component.js @@ -0,0 +1,67 @@ +const fs = require("fs"); +const path = require("path"); + +const componentsPath = path.join(__dirname, "../registry/components"); +const pagesPath = path.join(__dirname, "../app/components"); +const templatePath = path.join(__dirname, "../templates"); + +function toCamelCase(str) { + const words = str.split("-"); + return words + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(""); +} + +(async () => { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.log("\nPlease provide a component name as an argument.\n"); + console.log("pnpm add-component "); + return; + } + + const componentName = args[0]; + const componentNameCamelCase = toCamelCase(componentName); + const componentDir = path.join(componentsPath, componentName); + + if (fs.existsSync(componentDir)) { + console.log(`\nComponent "${componentName}" already exists!`); + return; + } + + const pageDir = path.join(pagesPath, componentName); + + if (fs.existsSync(pageDir)) { + console.log(`\nPage for "${componentName}" already exists!`); + return; + } + + // Create component and page directories + fs.mkdirSync(componentDir, { recursive: true }); + fs.mkdirSync(pageDir, { recursive: true }); + + // Copy template files to page and component directory + fs.cpSync(path.join(templatePath, "component"), componentDir, { + recursive: true, + }); + fs.cpSync(path.join(templatePath, "page"), pageDir, { recursive: true }); + + // Function for replacing all template strings + const replaceTemplateStrings = (filePath) => { + const file = fs.readFileSync(filePath, "utf8"); + const newFile = file + .replace(/\$CAMELCOMPONENT/g, componentNameCamelCase) + .replace(/\$COMPONENT/g, componentName); + fs.writeFileSync(filePath, newFile); + }; + + // Replace template strings + replaceTemplateStrings(path.join(componentDir, "index.tsx")); + replaceTemplateStrings(path.join(componentDir, "demo.tsx")); + replaceTemplateStrings(path.join(componentDir, "registry.json")); + + replaceTemplateStrings(path.join(pageDir, "page.tsx")); + + console.log(`\nComponent "${componentName}" created!`); +})(); diff --git a/apps/ui-components/tailwind.config.ts b/apps/ui-components/tailwind.config.ts new file mode 100644 index 000000000..2e567d9ab --- /dev/null +++ b/apps/ui-components/tailwind.config.ts @@ -0,0 +1,96 @@ +import type { Config } from "tailwindcss"; + +const config = { + darkMode: ["class"], + content: [ + "./pages/**/*.{ts,tsx}", + "./components/**/*.{ts,tsx}", + "./registry/**/*.{ts,tsx}", + "./app/**/*.{ts,tsx}", + "./src/**/*.{ts,tsx}", + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + chart: { + "1": "hsl(var(--chart-1))", + "2": "hsl(var(--chart-2))", + "3": "hsl(var(--chart-3))", + "4": "hsl(var(--chart-4))", + "5": "hsl(var(--chart-5))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { + height: "0", + }, + to: { + height: "var(--radix-accordion-content-height)", + }, + }, + "accordion-up": { + from: { + height: "var(--radix-accordion-content-height)", + }, + to: { + height: "0", + }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} satisfies Config; + +export default config; diff --git a/apps/ui-components/templates/component/demo.tsx b/apps/ui-components/templates/component/demo.tsx new file mode 100644 index 000000000..e909f725a --- /dev/null +++ b/apps/ui-components/templates/component/demo.tsx @@ -0,0 +1,21 @@ +import { Background, ReactFlow } from "@xyflow/react"; + +import { $CAMELCOMPONENT } from "@/registry/components/$COMPONENT"; + +const defaultNodes = [ + { + id: "1", + position: { x: 200, y: 200 }, + data: { label: "Node" }, + }, +]; + +export default function $CAMELCOMPONENTDemo() { + return ( +
+ + + +
+ ); +} diff --git a/apps/ui-components/templates/component/index.tsx b/apps/ui-components/templates/component/index.tsx new file mode 100644 index 000000000..de148fc2c --- /dev/null +++ b/apps/ui-components/templates/component/index.tsx @@ -0,0 +1,3 @@ +export function $CAMELCOMPONENT() { + return
; +} diff --git a/apps/ui-components/templates/component/registry.json b/apps/ui-components/templates/component/registry.json new file mode 100644 index 000000000..cc97374cb --- /dev/null +++ b/apps/ui-components/templates/component/registry.json @@ -0,0 +1,16 @@ +{ + "name": "$COMPONENT", + "type": "registry:component", + "dependencies": ["@xyflow/react"], + "devDependencies": [], + "registryDependencies": [], + "files": [ + { + "type": "registry:component", + "path": "$COMPONENT.tsx" + } + ], + "tailwind": {}, + "cssVars": {}, + "meta": {} +} diff --git a/apps/ui-components/templates/page/page.tsx b/apps/ui-components/templates/page/page.tsx new file mode 100644 index 000000000..fba3c0a9a --- /dev/null +++ b/apps/ui-components/templates/page/page.tsx @@ -0,0 +1,10 @@ +import DemoWrapper from "@/components/demo-wrapper"; +import Demo from "@/registry/components/$COMPONENT/demo"; + +export default function DemoPage() { + return ( + + + + ); +} diff --git a/apps/ui-components/tsconfig.json b/apps/ui-components/tsconfig.json new file mode 100644 index 000000000..407b2876b --- /dev/null +++ b/apps/ui-components/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules", "templates/**"] +} diff --git a/apps/xy-styleguide/.eslintrc.cjs b/apps/xy-styleguide/.eslintrc.cjs deleted file mode 100644 index 5a3965e69..000000000 --- a/apps/xy-styleguide/.eslintrc.cjs +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = { - env: { - browser: true, - es2020: true - }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module' - }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': 'warn' - } -}; \ No newline at end of file diff --git a/apps/xy-styleguide/.gitignore b/apps/xy-styleguide/.gitignore deleted file mode 100644 index a547bf36d..000000000 --- a/apps/xy-styleguide/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/apps/xy-styleguide/.storybook/main.ts b/apps/xy-styleguide/.storybook/main.ts deleted file mode 100644 index 6452ff3af..000000000 --- a/apps/xy-styleguide/.storybook/main.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { StorybookConfig } from '@storybook/react-vite'; -const config: StorybookConfig = { - stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-interactions', - { - name: '@storybook/addon-styling', - options: { - // Check out https://github.com/storybookjs/addon-styling/blob/main/docs/api.md - // For more details on this addon's options. - postCss: true, - }, - }, - ], - framework: { - name: '@storybook/react-vite', - options: {}, - }, - docs: { - autodocs: 'tag', - }, - staticDirs: ['../public'], -}; -export default config; diff --git a/apps/xy-styleguide/.storybook/preview-head.html b/apps/xy-styleguide/.storybook/preview-head.html deleted file mode 100644 index e810aa62b..000000000 --- a/apps/xy-styleguide/.storybook/preview-head.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - diff --git a/apps/xy-styleguide/.storybook/preview.ts b/apps/xy-styleguide/.storybook/preview.ts deleted file mode 100644 index 8ec9643be..000000000 --- a/apps/xy-styleguide/.storybook/preview.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Preview } from '@storybook/react'; - -import '../src/styles/tailwind.css'; - -const preview: Preview = { - parameters: { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, - }, -}; - -export default preview; diff --git a/apps/xy-styleguide/index.html b/apps/xy-styleguide/index.html deleted file mode 100644 index e0d1c8408..000000000 --- a/apps/xy-styleguide/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Vite + React + TS - - -
- - - diff --git a/apps/xy-styleguide/package.json b/apps/xy-styleguide/package.json deleted file mode 100644 index 6db1d0062..000000000 --- a/apps/xy-styleguide/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "xy-styleguide", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "storybook dev -p 6006", - "build": "tsc && vite build", - "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" - }, - "dependencies": { - "@heroicons/react": "^2.0.18", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "@xyflow/xy-ui": "workspace:*" - }, - "devDependencies": { - "@storybook/addon-essentials": "^7.0.22", - "@storybook/addon-interactions": "^7.0.22", - "@storybook/addon-links": "^7.0.22", - "@storybook/addon-styling": "^1.3.0", - "@storybook/blocks": "^7.0.22", - "@storybook/react": "^7.0.22", - "@storybook/react-vite": "^7.0.22", - "@storybook/testing-library": "^0.0.14-next.2", - "@types/react": "^18.0.37", - "@types/react-dom": "^18.0.11", - "@typescript-eslint/eslint-plugin": "^5.59.0", - "@typescript-eslint/parser": "^5.59.0", - "@vitejs/plugin-react-swc": "^3.0.0", - "autoprefixer": "^10.4.14", - "eslint": "^8.38.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.3.4", - "eslint-plugin-storybook": "^0.6.12", - "postcss": "^8.4.24", - "prop-types": "^15.8.1", - "storybook": "^7.0.22", - "tailwindcss": "^3.3.2", - "typescript": "^5.0.2", - "vite": "^4.3.9", - "xy-tailwind-config": "workspace:*" - } -} diff --git a/apps/xy-styleguide/postcss.config.js b/apps/xy-styleguide/postcss.config.js deleted file mode 100644 index aab2694ce..000000000 --- a/apps/xy-styleguide/postcss.config.js +++ /dev/null @@ -1,7 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - ...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}), - }, -}; diff --git a/apps/xy-styleguide/public/vite.svg b/apps/xy-styleguide/public/vite.svg deleted file mode 100644 index e7b8dfb1b..000000000 --- a/apps/xy-styleguide/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/xy-styleguide/src/App.css b/apps/xy-styleguide/src/App.css deleted file mode 100644 index b9d355df2..000000000 --- a/apps/xy-styleguide/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/apps/xy-styleguide/src/App.tsx b/apps/xy-styleguide/src/App.tsx deleted file mode 100644 index 7025ee4a0..000000000 --- a/apps/xy-styleguide/src/App.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState } from 'react'; -import reactLogo from './assets/react.svg'; -import viteLogo from '/vite.svg'; -import './App.css'; - -function App() { - const [count, setCount] = useState(0); - - return ( - <> - -

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ); -} - -export default App; diff --git a/apps/xy-styleguide/src/assets/react.svg b/apps/xy-styleguide/src/assets/react.svg deleted file mode 100644 index 6c87de9bb..000000000 --- a/apps/xy-styleguide/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/xy-styleguide/src/index.css b/apps/xy-styleguide/src/index.css deleted file mode 100644 index 2c3fac689..000000000 --- a/apps/xy-styleguide/src/index.css +++ /dev/null @@ -1,69 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/apps/xy-styleguide/src/main.tsx b/apps/xy-styleguide/src/main.tsx deleted file mode 100644 index 8c4462a73..000000000 --- a/apps/xy-styleguide/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App.tsx'; -import './index.css'; - -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - - - , -); diff --git a/apps/xy-styleguide/src/stories/Accordion.stories.tsx b/apps/xy-styleguide/src/stories/Accordion.stories.tsx deleted file mode 100644 index e0a5d3df2..000000000 --- a/apps/xy-styleguide/src/stories/Accordion.stories.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { - Accordion, - AccordionItem, - AccordionTrigger, - AccordionContent, -} from '@xyflow/xy-ui'; - -const meta = { - title: 'Example/Accordion', - component: Accordion, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Single: Story = { - name: 'Single Accordion', - args: { - type: 'single', - }, - render: () => ( - - - List item heading 1 - List item content - - - List item heading 2 - List item content - - - List item heading 3 - List item content - - - ), -}; - -export const Multi: Story = { - name: 'Multi Accordion', - args: { - type: 'multiple', - }, - render: () => ( - - - List item heading 1 - List item content - - - List item heading 2 - List item content - - - List item heading 3 - List item content - - - ), -}; diff --git a/apps/xy-styleguide/src/stories/Button.stories.tsx b/apps/xy-styleguide/src/stories/Button.stories.tsx deleted file mode 100644 index cf99001db..000000000 --- a/apps/xy-styleguide/src/stories/Button.stories.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { Button } from '@xyflow/xy-ui'; - -// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction -const meta = { - title: 'Example/Button', - component: Button, - tags: ['autodocs'], - argTypes: {}, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -export const Primary: Story = { - args: { - variant: 'default', - children: 'Button', - }, -}; - -export const Secondary: Story = { - args: { - variant: 'secondary', - children: 'Button', - }, -}; - -export const Ghost: Story = { - args: { - variant: 'ghost', - children: 'Ghost', - }, -}; - -export const Large: Story = { - args: { - size: 'lg', - children: 'Button', - }, -}; - -export const Small: Story = { - args: { - size: 'sm', - children: 'Button', - }, -}; - -export const React: Story = { - args: { - variant: 'react', - children: 'React Flow', - }, -}; - -export const Svelte: Story = { - args: { - variant: 'svelte', - children: 'Svelte Flow', - }, -}; diff --git a/apps/xy-styleguide/src/stories/Checkbox.stories.tsx b/apps/xy-styleguide/src/stories/Checkbox.stories.tsx deleted file mode 100644 index 2624fd3fa..000000000 --- a/apps/xy-styleguide/src/stories/Checkbox.stories.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { Checkbox } from '@xyflow/xy-ui'; - -// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction -const meta = { - title: 'Example/Checkbox', - component: Checkbox, - tags: ['autodocs'], - argTypes: {}, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -export const Primary: Story = { - args: { - children: 'Test Label', - }, -}; diff --git a/apps/xy-styleguide/src/stories/Container.stories.tsx b/apps/xy-styleguide/src/stories/Container.stories.tsx deleted file mode 100644 index 1149f8c23..000000000 --- a/apps/xy-styleguide/src/stories/Container.stories.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { Container } from '@xyflow/xy-ui'; - -const meta = { - title: 'Example/Container', - component: Container, - tags: ['autodocs'], - argTypes: {}, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - name: 'Default Container', - render: () => ( - -
some content
-
- ), -}; - -export const Dark: Story = { - name: 'Dark Container', - render: () => ( - -
some content
-
- ), -}; diff --git a/apps/xy-styleguide/src/stories/Introduction.mdx b/apps/xy-styleguide/src/stories/Introduction.mdx deleted file mode 100644 index ff7fc71fb..000000000 --- a/apps/xy-styleguide/src/stories/Introduction.mdx +++ /dev/null @@ -1,213 +0,0 @@ -import { Meta } from '@storybook/blocks'; -import Code from './assets/code-brackets.svg'; -import Colors from './assets/colors.svg'; -import Comments from './assets/comments.svg'; -import Direction from './assets/direction.svg'; -import Flow from './assets/flow.svg'; -import Plugin from './assets/plugin.svg'; -import Repo from './assets/repo.svg'; -import StackAlt from './assets/stackalt.svg'; - - - - - -# Welcome to Storybook - -Storybook helps you build UI components in isolation from your app's business logic, data, and context. -That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA. - -Browse example stories now by navigating to them in the sidebar. -View their code in the `stories` directory to learn how they work. -We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages. - -
Configure
- - - -
Learn
- - - -
- TipEdit the Markdown in{' '} - stories/Introduction.stories.mdx -
diff --git a/apps/xy-styleguide/src/stories/ListWrapper.stories.tsx b/apps/xy-styleguide/src/stories/ListWrapper.stories.tsx deleted file mode 100644 index 11c7d0ade..000000000 --- a/apps/xy-styleguide/src/stories/ListWrapper.stories.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { InformationCircleIcon } from '@heroicons/react/24/outline'; -import type { Meta, StoryObj } from '@storybook/react'; - -import { ListWrapper } from '@xyflow/xy-ui'; - -const meta = { - title: 'Example/ListWrapper', - component: ListWrapper, - tags: ['autodocs'], - argTypes: {}, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - name: 'Select with placeholder', - args: { - icon: InformationCircleIcon, - title: 'List title', - subtitle: 'Some description', - children: ( -
    -
  • - list item description text lorem ipsum list item description text - lorem ipsum -
  • -
  • - list item description text lorem ipsum list item description text -
  • -
  • - list item description text lorem ipsum list item description text - lorem -
  • -
  • - list item description text lorem ipsum list item description text - lorem ipsum -
  • -
- ), - }, -}; diff --git a/apps/xy-styleguide/src/stories/PricingTable.stories.tsx b/apps/xy-styleguide/src/stories/PricingTable.stories.tsx deleted file mode 100644 index 4815b9316..000000000 --- a/apps/xy-styleguide/src/stories/PricingTable.stories.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { PricingTable } from '@xyflow/xy-ui'; - -const meta = { - title: 'Example/PricingTable', - component: PricingTable, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - name: 'Default Pricing Table', - render: () => , -}; diff --git a/apps/xy-styleguide/src/stories/RadioGroup.stories.tsx b/apps/xy-styleguide/src/stories/RadioGroup.stories.tsx deleted file mode 100644 index 1e7d883b8..000000000 --- a/apps/xy-styleguide/src/stories/RadioGroup.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { RadioGroup, RadioGroupItem, Label } from '@xyflow/xy-ui'; - -// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction -const meta = { - title: 'Example/RadioGroup', - component: RadioGroup, - tags: ['autodocs'], - argTypes: {}, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -export const Primary: Story = { - args: { - children: ( - <> -
- - test - - -
-
- - test2 - - -
- - ), - }, -}; diff --git a/apps/xy-styleguide/src/stories/Select.stories.tsx b/apps/xy-styleguide/src/stories/Select.stories.tsx deleted file mode 100644 index 657444e57..000000000 --- a/apps/xy-styleguide/src/stories/Select.stories.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@xyflow/xy-ui'; - -const meta = { - title: 'Example/Select', - component: Select, - tags: ['autodocs'], - argTypes: {}, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Placeholder: Story = { - name: 'Select with placeholder', - render: () => ( - - ), -}; - -export const DefaultValue: Story = { - name: 'Select with default value', - render: () => ( - - ), -}; diff --git a/apps/xy-styleguide/src/stories/assets/code-brackets.svg b/apps/xy-styleguide/src/stories/assets/code-brackets.svg deleted file mode 100644 index 73de94776..000000000 --- a/apps/xy-styleguide/src/stories/assets/code-brackets.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/code-brackets \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/colors.svg b/apps/xy-styleguide/src/stories/assets/colors.svg deleted file mode 100644 index 17d58d516..000000000 --- a/apps/xy-styleguide/src/stories/assets/colors.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/colors \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/comments.svg b/apps/xy-styleguide/src/stories/assets/comments.svg deleted file mode 100644 index 6493a139f..000000000 --- a/apps/xy-styleguide/src/stories/assets/comments.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/comments \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/direction.svg b/apps/xy-styleguide/src/stories/assets/direction.svg deleted file mode 100644 index 65676ac27..000000000 --- a/apps/xy-styleguide/src/stories/assets/direction.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/direction \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/flow.svg b/apps/xy-styleguide/src/stories/assets/flow.svg deleted file mode 100644 index 8ac27db40..000000000 --- a/apps/xy-styleguide/src/stories/assets/flow.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/flow \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/plugin.svg b/apps/xy-styleguide/src/stories/assets/plugin.svg deleted file mode 100644 index 29e5c690c..000000000 --- a/apps/xy-styleguide/src/stories/assets/plugin.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/plugin \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/repo.svg b/apps/xy-styleguide/src/stories/assets/repo.svg deleted file mode 100644 index f386ee902..000000000 --- a/apps/xy-styleguide/src/stories/assets/repo.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/repo \ No newline at end of file diff --git a/apps/xy-styleguide/src/stories/assets/stackalt.svg b/apps/xy-styleguide/src/stories/assets/stackalt.svg deleted file mode 100644 index 9b7ad2743..000000000 --- a/apps/xy-styleguide/src/stories/assets/stackalt.svg +++ /dev/null @@ -1 +0,0 @@ -illustration/stackalt \ No newline at end of file diff --git a/apps/xy-styleguide/src/styles/tailwind.css b/apps/xy-styleguide/src/styles/tailwind.css deleted file mode 100644 index 4dc44e637..000000000 --- a/apps/xy-styleguide/src/styles/tailwind.css +++ /dev/null @@ -1,56 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --color-xyflow: 248 1% 85%; - --color-react: 333 100% 50%; - --color-svelte: 15 100% 50%; - - --background: 0 0% 100%; - --foreground: 222.2 47.4% 11.2%; - - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - - --popover: 0 0% 100%; - --popover-foreground: 222.2 47.4% 11.2%; - - --card: 0 0% 100%; - --card-foreground: 222.2 47.4% 11.2%; - - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - - --secondary: 2010 100% 100%; - --secondary-foreground: 222.2 47.4% 11.2%; - - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - - --destructive: 0 100% 50%; - --destructive-foreground: 210 40% 98%; - - --ring: 215 20.2% 65.1%; - - --radius: 0.5rem; - } -} - -@layer base { - * { - box-sizing: border-box; - } - body { - @apply bg-background text-foreground; - font-feature-settings: 'rlig' 1, 'calt' 1; - } - html { - /* this is a nextra default we need to overwrite */ - font-feature-settings: normal !important; - } -} diff --git a/apps/xy-styleguide/src/vite-env.d.ts b/apps/xy-styleguide/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2a..000000000 --- a/apps/xy-styleguide/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/apps/xy-styleguide/tailwind.config.js b/apps/xy-styleguide/tailwind.config.js deleted file mode 100644 index c963f3517..000000000 --- a/apps/xy-styleguide/tailwind.config.js +++ /dev/null @@ -1,16 +0,0 @@ -import xyTailwindConfig from 'xy-tailwind-config'; - -/** @type {import('tailwindcss').Config} */ -export default { - ...xyTailwindConfig, - theme: { - container: xyTailwindConfig.theme.container, - extend: { - ...xyTailwindConfig.theme.extend, - fontFamily: { - sans: ['NTDapper'], - mono: ['var(--font-firamono)'], - }, - }, - }, -}; diff --git a/apps/xy-styleguide/tsconfig.json b/apps/xy-styleguide/tsconfig.json deleted file mode 100644 index a7fc6fbf2..000000000 --- a/apps/xy-styleguide/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/apps/xy-styleguide/tsconfig.node.json b/apps/xy-styleguide/tsconfig.node.json deleted file mode 100644 index 42872c59f..000000000 --- a/apps/xy-styleguide/tsconfig.node.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/apps/xy-styleguide/vite.config.ts b/apps/xy-styleguide/vite.config.ts deleted file mode 100644 index 5c69c451b..000000000 --- a/apps/xy-styleguide/vite.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react-swc'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - define: { 'process.env': {} }, -}); diff --git a/packages/eslint-config-xyflow/index.js b/configs/eslint-config-xyflow/index.js similarity index 100% rename from packages/eslint-config-xyflow/index.js rename to configs/eslint-config-xyflow/index.js diff --git a/configs/eslint-config-xyflow/package.json b/configs/eslint-config-xyflow/package.json new file mode 100644 index 000000000..4e45a7ae4 --- /dev/null +++ b/configs/eslint-config-xyflow/package.json @@ -0,0 +1,16 @@ +{ + "name": "eslint-config-xyflow", + "version": "0.0.0", + "private": true, + "main": "index.js", + "license": "MIT", + "dependencies": { + "eslint-config-next": "^14.2.15", + "eslint-config-prettier": "^9.1.0", + "eslint-config-turbo": "^2.2.3", + "eslint-plugin-react": "7.37.2" + }, + "devDependencies": { + "typescript": "^5.5.4" + } +} diff --git a/packages/xy-tailwind-config/index.js b/configs/xy-tailwind-config/index.js similarity index 100% rename from packages/xy-tailwind-config/index.js rename to configs/xy-tailwind-config/index.js diff --git a/packages/xy-tailwind-config/package.json b/configs/xy-tailwind-config/package.json similarity index 83% rename from packages/xy-tailwind-config/package.json rename to configs/xy-tailwind-config/package.json index 041f1c929..385bde1b3 100644 --- a/packages/xy-tailwind-config/package.json +++ b/configs/xy-tailwind-config/package.json @@ -5,6 +5,6 @@ "main": "index.js", "license": "MIT", "devDependencies": { - "tailwindcss": "^3.3.3" + "tailwindcss": "^3.4.14" } } diff --git a/packages/xy-tsconfig/base.json b/configs/xy-tsconfig/base.json similarity index 100% rename from packages/xy-tsconfig/base.json rename to configs/xy-tsconfig/base.json diff --git a/packages/xy-tsconfig/nextjs.json b/configs/xy-tsconfig/nextjs.json similarity index 100% rename from packages/xy-tsconfig/nextjs.json rename to configs/xy-tsconfig/nextjs.json diff --git a/packages/xy-tsconfig/package.json b/configs/xy-tsconfig/package.json similarity index 100% rename from packages/xy-tsconfig/package.json rename to configs/xy-tsconfig/package.json diff --git a/packages/xy-tsconfig/react-library.json b/configs/xy-tsconfig/react-library.json similarity index 100% rename from packages/xy-tsconfig/react-library.json rename to configs/xy-tsconfig/react-library.json diff --git a/package.json b/package.json index 4f401311a..b6b1fd92e 100644 --- a/package.json +++ b/package.json @@ -4,28 +4,27 @@ "preinstall": "npx only-allow pnpm", "build": "turbo run build", "dev": "turbo run dev", - "dev:docs": "turbo run dev --filter reactflow-website --filter svelteflow-website --filter example-apps", + "dev:docs": "turbo run dev --filter reactflow-website --filter svelteflow-website --filter example-apps --filter ui-components", "dev:reactflow.dev": "turbo run dev --filter reactflow-website --filter example-apps", "dev:svelteflow.dev": "turbo run dev --filter svelteflow-website --filter example-apps", + "dev:ui-components": "turbo run dev --filter ui-components", "dev:xyflow.com": "turbo run dev --filter xyflow-website --filter example-apps", - "dev:style": "turbo run dev --filter xy-styleguide", "lint": "turbo run lint", "format": "prettier --write \"**/*.{ts,tsx,md}\"", - "clean": "pnpm -r --parallel exec rimraf dist .turbo node_modules", - "whatsnew": "cd scripts/create-whats-new && pnpm start" + "clean": "pnpm -r --parallel exec rimraf dist .turbo node_modules" }, "devDependencies": { - "@turbo/gen": "^1.10.14", - "eslint": "^8.49.0", + "@turbo/gen": "^2.2.3", + "eslint": "^8.57.1", "eslint-config-xyflow": "workspace:*", - "prettier": "^3.0.3", - "rimraf": "^5.0.1", - "turbo": "^1.10.14", - "typescript": "^5.2.2" + "prettier": "^3.3.3", + "rimraf": "^6.0.1", + "turbo": "^2.2.3", + "typescript": "^5.5.4" }, - "packageManager": "pnpm@9.1.4", + "packageManager": "pnpm@9.12.2", "name": "docs", "dependencies": { - "dotenv-cli": "^7.3.0" + "dotenv-cli": "^7.4.2" } } diff --git a/packages/eslint-config-xyflow/package.json b/packages/eslint-config-xyflow/package.json deleted file mode 100644 index e4e7f0e25..000000000 --- a/packages/eslint-config-xyflow/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "eslint-config-xyflow", - "version": "0.0.0", - "private": true, - "main": "index.js", - "license": "MIT", - "dependencies": { - "eslint-config-next": "^13.4.19", - "eslint-config-prettier": "^9.0.0", - "eslint-config-turbo": "^1.10.12", - "eslint-plugin-react": "7.33.2" - }, - "devDependencies": { - "typescript": "^5.1.3" - } -} diff --git a/packages/xy-shared/components/head.tsx b/packages/xy-shared/components/head.tsx deleted file mode 100644 index a1df58dd5..000000000 --- a/packages/xy-shared/components/head.tsx +++ /dev/null @@ -1,49 +0,0 @@ -export function Head({ - title, - description, - pageUrl, - faviconUrl, - ogImage, - framework, -}: { - title: string; - description: string; - pageUrl: string; - faviconUrl: string; - ogImage: { - url: string; - width?: string; - height?: string; - }; - framework?: string; -}) { - return ( - <> - {title} - - - - - - - - - - - - - - - {ogImage && ( - <> - - - - - - )} - - {framework ? : null} - - ); -} diff --git a/packages/xy-shared/components/sidebar-title.tsx b/packages/xy-shared/components/sidebar-title.tsx index 4b1d21d4f..c9f0a637e 100644 --- a/packages/xy-shared/components/sidebar-title.tsx +++ b/packages/xy-shared/components/sidebar-title.tsx @@ -1,5 +1,5 @@ import { cn } from '@xyflow/xy-ui'; -import { getFrontmatterTag } from '../lib/utils'; +import { getFrontmatterTag } from '../lib'; const NUM_DAYS_NEW = 30; const DAYS_IN_MS = 1000 * 3600 * 24; diff --git a/packages/xy-shared/context/shared-context.tsx b/packages/xy-shared/context/shared-context.tsx new file mode 100644 index 000000000..ee1a1090f --- /dev/null +++ b/packages/xy-shared/context/shared-context.tsx @@ -0,0 +1,11 @@ +import { useConfig } from 'nextra-theme-docs'; +import { useData } from 'nextra/hooks'; + +import { createContext } from 'react'; + +type SharedContext = { + useConfig: typeof useConfig; + useData: typeof useData; +}; + +export const SharedContext = createContext({} as SharedContext); diff --git a/apps/xy-styleguide/public/fonts/NTDapper-black.woff2 b/packages/xy-shared/fonts/NTDapper-black.woff2 similarity index 100% rename from apps/xy-styleguide/public/fonts/NTDapper-black.woff2 rename to packages/xy-shared/fonts/NTDapper-black.woff2 diff --git a/apps/xy-styleguide/public/fonts/NTDapper-bold.woff2 b/packages/xy-shared/fonts/NTDapper-bold.woff2 similarity index 100% rename from apps/xy-styleguide/public/fonts/NTDapper-bold.woff2 rename to packages/xy-shared/fonts/NTDapper-bold.woff2 diff --git a/apps/xy-styleguide/public/fonts/NTDapper-medium.woff2 b/packages/xy-shared/fonts/NTDapper-medium.woff2 similarity index 100% rename from apps/xy-styleguide/public/fonts/NTDapper-medium.woff2 rename to packages/xy-shared/fonts/NTDapper-medium.woff2 diff --git a/apps/xy-styleguide/public/fonts/NTDapper-regular.woff2 b/packages/xy-shared/fonts/NTDapper-regular.woff2 similarity index 100% rename from apps/xy-styleguide/public/fonts/NTDapper-regular.woff2 rename to packages/xy-shared/fonts/NTDapper-regular.woff2 diff --git a/packages/xy-shared/fonts/index.ts b/packages/xy-shared/fonts/index.ts new file mode 100644 index 000000000..a9a5d5134 --- /dev/null +++ b/packages/xy-shared/fonts/index.ts @@ -0,0 +1,20 @@ +import localFont from 'next/font/local'; +import { Fira_Mono } from 'next/font/google'; + +export const ntDapperFont = localFont({ + src: [ + { path: './NTDapper-regular.woff2', weight: '400' }, + { path: './NTDapper-medium.woff2', weight: '500' }, + { path: './NTDapper-bold.woff2', weight: '700' }, + { path: './NTDapper-black.woff2', weight: '900' }, + ], + variable: '--font-ntdapper', +}); + +export const firaMonoFont = Fira_Mono({ + weight: ['400'], + subsets: ['latin'], + variable: '--font-firamono', +}); + +export const fontClassNames = `${ntDapperFont.variable} ${firaMonoFont.variable} font-sans`; diff --git a/packages/xy-shared/index.tsx b/packages/xy-shared/index.tsx index 21e9136bb..b3bb08373 100644 --- a/packages/xy-shared/index.tsx +++ b/packages/xy-shared/index.tsx @@ -1,11 +1,13 @@ export * from './layouts/404'; export * from './layouts/base'; +export * from './layouts/blog-post-base'; export * from './layouts/blog-post'; export * from './layouts/breakout'; export * from './layouts/case-study'; export * from './layouts/example'; export * from './layouts/showcase'; export * from './layouts/text-only'; +export * from './layouts/tutorial'; export * from './widgets/about-section'; export * from './widgets/authors-list'; @@ -33,6 +35,9 @@ export * from './components/node-wrapper'; export * from './components/timeline-event'; export * from './components/sidebar-title'; export * from './components/remote-content'; -export * from './components/head'; -export * from './lib/utils'; +export * from './lib'; + +export * from './context/shared-context'; + +export * from './types'; diff --git a/packages/xy-shared/layouts/blog-post-base.tsx b/packages/xy-shared/layouts/blog-post-base.tsx new file mode 100644 index 000000000..15c2fe26b --- /dev/null +++ b/packages/xy-shared/layouts/blog-post-base.tsx @@ -0,0 +1,91 @@ +import { type ReactNode } from 'react'; +import { type MdxFile } from 'nextra'; +import { Text, Heading, ContentGrid, ContentGridItem } from '@xyflow/xy-ui'; + +import { type Author, SubscribeSection, AuthorList, BlogPostPreview } from '..'; + +export type BlogPostFrontmatter = { + title: string; + htmlTitle?: string; + intro: string; + date: string; + authors: Author[]; +}; + +export type BlogPostLayoutProps = { + frontMatter: BlogPostFrontmatter; + prev?: MdxFile; + next?: MdxFile; + children: ReactNode; +}; + +export function BaseBlogPostLayout({ + frontMatter, + prev, + next, + children, +}: BlogPostLayoutProps) { + return ( +
+ + {frontMatter.date} + + {/* we have to use important (!) here to overwrite the nextra article default styles */} + + {frontMatter.htmlTitle ? ( + + ) : ( + frontMatter.title + )} + + + +
+ {children} +
+ +
+ +
+ + +
+ ); +} + +type BlogPostPreviewsProps = { + prev?: MdxFile; + next?: MdxFile; +}; + +function BlogPostPreviews({ prev, next }: BlogPostPreviewsProps) { + return ( +
+ + {prev && ( + + + + )} + + {next && ( + + + + )} + +
+ ); +} diff --git a/packages/xy-shared/layouts/blog-post.tsx b/packages/xy-shared/layouts/blog-post.tsx index a7a775656..fbd68b4bc 100644 --- a/packages/xy-shared/layouts/blog-post.tsx +++ b/packages/xy-shared/layouts/blog-post.tsx @@ -1,96 +1,21 @@ -import { type ReactNode } from 'react'; -import { type MdxFile } from 'nextra'; -import { Text, Heading, ContentGrid, ContentGridItem } from '@xyflow/xy-ui'; +import { useContext } from 'react'; +import { getPrevAndNextPagesByTitle } from '../lib'; +import { BaseBlogPostLayout, BlogPostFrontmatter } from './blog-post-base'; +import { SharedContext } from '../context/shared-context'; -import { - type Author, - SubscribeSection, - AuthorList, - BlogPostPreview, -} from '../'; - -export type BlogPostFrontmatter = { - title: string; - htmlTitle?: string; - intro: string; - date: string; - authors: Author[]; -}; - -export type BlogPostLayoutProps = { - frontMatter: BlogPostFrontmatter; - prev?: MdxFile; - next?: MdxFile; - children: ReactNode; +type BlogPostLayoutProps = { + children: React.ReactNode; }; -export function BlogPostLayout({ - frontMatter, - prev, - next, - children, -}: BlogPostLayoutProps) { - return ( -
- - {frontMatter.date} - - {/* we have to use important (!) here to overwrite the nextra article default styles */} - - {frontMatter.htmlTitle ? ( - - ) : ( - frontMatter.title - )} - - - -
- {children} -
+export function BlogPostLayout({ children }: BlogPostLayoutProps) { + const { useConfig } = useContext(SharedContext); + const { title, frontMatter } = useConfig(); -
- -
+ const { prev, next } = getPrevAndNextPagesByTitle(title, '/blog'); - -
- ); -} - -type BlogPostPreviewsProps = { - prev?: MdxFile; - next?: MdxFile; -}; - -function BlogPostPreviews({ prev, next }: BlogPostPreviewsProps) { return ( -
- - {prev && ( - - - - )} - - {next && ( - - - - )} - -
+ + <>{children} + ); } diff --git a/packages/xy-shared/layouts/case-study.tsx b/packages/xy-shared/layouts/case-study.tsx index 9d6db3794..4eb79aa1b 100644 --- a/packages/xy-shared/layouts/case-study.tsx +++ b/packages/xy-shared/layouts/case-study.tsx @@ -1,4 +1,4 @@ -import { type ReactNode } from 'react'; +import { useContext, type ReactNode } from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { type MdxFile } from 'nextra'; @@ -11,7 +11,14 @@ import { ContentGridItem, } from '@xyflow/xy-ui'; -import { AuthorList, Author, ProjectPreview, SubscribeSection } from '../'; +import { + AuthorList, + Author, + ProjectPreview, + SubscribeSection, + SharedContext, + getPrevAndNextPagesByTitle, +} from '../'; export type CaseStudyFrontmatter = { title: string; @@ -27,18 +34,14 @@ export type CaseStudyFrontmatter = { }; export type CaseStudyLayoutProps = { - frontMatter: CaseStudyFrontmatter; - prev?: MdxFile; - next?: MdxFile; children: ReactNode; }; -export function CaseStudyLayout({ - frontMatter, - prev, - next, - children, -}: CaseStudyLayoutProps) { +export function CaseStudyLayout({ children }: CaseStudyLayoutProps) { + const { useConfig } = useContext(SharedContext); + const { title, frontMatter } = useConfig(); + + const { prev, next } = getPrevAndNextPagesByTitle(title, '/pro/case-studies'); return ( <>
diff --git a/packages/xy-shared/layouts/example.tsx b/packages/xy-shared/layouts/example.tsx index ac7197cc3..df51a5c97 100644 --- a/packages/xy-shared/layouts/example.tsx +++ b/packages/xy-shared/layouts/example.tsx @@ -1,7 +1,7 @@ -import { type ReactNode } from 'react'; +import { SharedContext } from '../context/shared-context'; +import { useContext, type ReactNode } from 'react'; -export type ExampleLayoutProps = { - frontMatter: { title: string; is_pro_example?: boolean; is_free?: boolean }; +type ExampleLayoutProps = { children: ReactNode; }; @@ -10,18 +10,18 @@ export type ExampleLayoutProps = { * svelteflow.dev. * */ -export function ExampleLayout({ frontMatter, children }: ExampleLayoutProps) { +export function ExampleLayout({ children }: ExampleLayoutProps) { + const { useConfig } = useContext(SharedContext); + const { frontMatter } = useConfig<{ + title: string; + }>(); + return ( <>

{frontMatter.title}

- {frontMatter.is_pro_example && ( -
- Pro {frontMatter.is_free && 'Trial'} -
- )}
{children} diff --git a/packages/xy-shared/layouts/tutorial.tsx b/packages/xy-shared/layouts/tutorial.tsx new file mode 100644 index 000000000..1d90ec570 --- /dev/null +++ b/packages/xy-shared/layouts/tutorial.tsx @@ -0,0 +1,22 @@ +import { ReactNode, useContext } from 'react'; +import { BaseBlogPostLayout, BlogPostFrontmatter } from './blog-post-base'; +import { getPrevAndNextPagesByTitle } from '../lib'; +import { SharedContext } from '../context/shared-context'; + +export type TutorialLayoutProps = { + children: ReactNode; +}; + +export function TutorialLayout({ children }: TutorialLayoutProps) { + const { useConfig } = useContext(SharedContext); + const { title, frontMatter } = useConfig(); + // const { prev, next } = getPrevAndNextPagesByTitle(title, '/learn/tutorials'); + + return ( + + <>{children} + + ); +} + +export default TutorialLayout; diff --git a/packages/xy-shared/lib/utils.tsx b/packages/xy-shared/lib/index.tsx similarity index 73% rename from packages/xy-shared/lib/utils.tsx rename to packages/xy-shared/lib/index.tsx index 2ca03d299..26dfaee20 100644 --- a/packages/xy-shared/lib/utils.tsx +++ b/packages/xy-shared/lib/index.tsx @@ -1,6 +1,6 @@ -import { ReactNode } from 'react'; -import { Folder, MdxFile, Page } from 'nextra'; -import { getAllPages } from 'nextra/context'; +import type { ReactNode } from 'react'; +import type { Folder, MdxFile, Page } from 'nextra'; +import { getAllPages, getPagesUnderRoute } from 'nextra/context'; import { SidebarTitle } from '../components/sidebar-title'; @@ -107,3 +107,41 @@ export function getMetaConfigFromTitleLookup( return acc; }, {}); } + +export function getMdxPagesUnderRoute( + route: InternalRoute, +) { + return getPagesUnderRoute(route).filter(isMdxFile); +} + +// used for pagination for blog and case studies to display prev and next post/ case study +export function getPrevAndNextPagesByTitle( + title: string, + route: InternalRoute, +) { + const pages = getMdxPagesUnderRoute(route); + + const currentIndex = pages.findIndex( + (page) => page.frontMatter?.title === title, + ); + const prevIndex = currentIndex === 0 ? pages.length - 1 : currentIndex - 1; + const nextIndex = currentIndex === pages.length - 1 ? 0 : currentIndex + 1; + + const prev = pages[prevIndex]; + const next = pages[nextIndex]; + + return { prev, next }; +} + +export async function fetchJSON(url: string): Promise> { + let json = {}; + + try { + const resp = await fetch(url, { headers: { 'User-Agent': 'webkid' } }); + json = await resp.json(); + } catch (err) { + console.log(err); + } + + return json; +} diff --git a/packages/xy-shared/package.json b/packages/xy-shared/package.json index 77091896b..02da4aa8a 100644 --- a/packages/xy-shared/package.json +++ b/packages/xy-shared/package.json @@ -10,49 +10,46 @@ "generate:component": "turbo gen react-component" }, "devDependencies": { - "@types/react": "^18.2.14", - "@types/react-dom": "^18.2.6", - "eslint": "^8.43.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "eslint": "^8.57.1", "eslint-config-xyflow": "workspace:*", - "react": "^18.2.0", - "typescript": "^5.1.3", + "react": "^18.3.1", + "typescript": "^5.5.4", "xy-tsconfig": "workspace:*" }, "dependencies": { - "@codesandbox/sandpack-react": "^2.18.2", - "@docsearch/css": "^3.5.2", - "@docsearch/react": "^3.5.2", - "@heroicons/react": "^2.0.18", - "@radix-ui/react-accordion": "^1.1.2", - "@radix-ui/react-alert-dialog": "^1.0.5", - "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-popover": "^1.0.7", - "@radix-ui/react-radio-group": "^1.1.3", - "@radix-ui/react-select": "^2.0.0", - "@radix-ui/react-slider": "^1.1.2", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-tabs": "^1.0.4", - "@react-three/fiber": "^8.13.7", + "@codesandbox/sandpack-react": "^2.19.9", + "@docsearch/css": "^3.6.2", + "@docsearch/react": "^3.6.2", + "@heroicons/react": "^2.1.5", + "@radix-ui/react-accordion": "^1.2.1", + "@radix-ui/react-alert-dialog": "^1.1.2", + "@radix-ui/react-checkbox": "^1.1.2", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-radio-group": "^1.2.1", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-slider": "^1.2.1", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.1", + "@react-three/fiber": "^8.17.10", "@sindresorhus/slugify": "^2.2.1", - "@stackblitz/sdk": "^1.9.0", - "@xyflow/react": "^12.0.0-next.19", + "@stackblitz/sdk": "^1.11.0", + "@xyflow/react": "^12.3.2", "@xyflow/xy-ui": "workspace:*", - "class-variance-authority": "^0.6.0", - "clsx": "^1.2.1", - "fathom-client": "^3.5.0", - "next": "^13.5.3", - "react-player": "^2.12.0", - "reactflow": "^11.10.1", - "tailwind-merge": "^1.13.2", - "tailwindcss": "^3.3.2", - "tailwindcss-animate": "^1.0.6", + "clsx": "^2.1.1", + "fathom-client": "^3.7.2", + "next": "^14.2.15", + "nextra": "^3.0.15", + "nextra-theme-docs": "^3.0.15", + "react-player": "^2.16.0", + "tailwindcss": "^3.4.14", + "tailwindcss-animate": "^1.0.7", "timeago-react": "^3.0.6", "use-error-boundary": "^2.0.6" }, "peerDependencies": { - "nextra": "^3.0.4", - "nextra-theme-docs": "^3.0.4", "react": "^18.2.0", "react-dom": "^18.2.0" } diff --git a/packages/xy-shared/server/compile-code-snippet.ts b/packages/xy-shared/server/compile-code-snippet.ts new file mode 100644 index 000000000..7a0bd6e58 --- /dev/null +++ b/packages/xy-shared/server/compile-code-snippet.ts @@ -0,0 +1,47 @@ +import { compileMdx } from 'nextra/compile'; +import { CompiledMdx } from '../types'; + +type CompileCodeSnippetOptions = { + filetype?: string; + showCopy?: boolean; + showLineNumbers?: boolean; + highlight?: string; + filename?: string; + npm2yarn?: boolean; +}; + +const defaultOptions = { + filetype: 'js', + showCopy: false, + showLineNumbers: false, + highlight: '', + filename: '', + npm2yarn: false, +}; + +function createMDXString(snippet: string, options: CompileCodeSnippetOptions) { + return ( + '```' + + options.filetype + + (options.showCopy ? ' copy ' : '') + + (options.showLineNumbers ? ' showLineNumbers' : '') + + (options.highlight ? ` /${options.highlight}/ ` : '') + + (options.filename ? ` filename="${options.filename}" ` : '') + + (options.npm2yarn ? ' npm2yarn' : '') + + '\n' + + snippet + ); +} + +export async function compileCodeSnippet( + snippet: string, + options?: CompileCodeSnippetOptions, +): Promise { + const opts = { ...defaultOptions, ...options }; + + const { result: compiledSource, frontMatter } = await compileMdx( + createMDXString(snippet, opts), + ); + + return { compiledSource, frontMatter }; +} diff --git a/packages/xy-shared/server/get-static-code.ts b/packages/xy-shared/server/get-static-code.ts new file mode 100644 index 000000000..f249abb42 --- /dev/null +++ b/packages/xy-shared/server/get-static-code.ts @@ -0,0 +1,46 @@ +import { Framework } from '@xyflow/xy-ui'; +import { compileCodeSnippet } from './compile-code-snippet'; +import { CompiledMdx, ExampleCode } from '../types'; +import { loadJSONFile } from './utils'; + +import path from 'path'; + +export function getStaticCode(routes: string[], framework?: Framework) { + return async () => { + const _framework = + framework ?? process.env.NEXT_PUBLIC_FRAMEWORK ?? 'react'; + const codeSnippets: Record> = {}; + + for (const route of routes) { + const files: Record = {}; + const url = `${process.env.NEXT_PUBLIC_EXAMPLES_URL}/${_framework}/${route}/source.json`; + + const p = path.join( + '../../apps/example-apps/public', + _framework, + route, + 'source.json', + ); + + const json = loadJSONFile(p); + + if (!json || !('files' in json) || !('dependencies' in json)) { + continue; + } + + for (const [filename, file] of Object.entries(json.files)) { + const filetype = filename.split('.').pop(); + const compiledSnippet = await compileCodeSnippet(file, { filetype }); + files[filename] = compiledSnippet; + } + + codeSnippets[route] = files; + } + + return { + props: { + codeSnippets, + }, + }; + }; +} diff --git a/packages/xy-shared/server/index.ts b/packages/xy-shared/server/index.ts new file mode 100644 index 000000000..ba7ee10cf --- /dev/null +++ b/packages/xy-shared/server/index.ts @@ -0,0 +1,4 @@ +export * from './compile-code-snippet'; +export * from './get-static-code'; +export * from './mdx-content-under-route'; +export * from './utils'; diff --git a/sites/reactflow.dev/src/utils/get-static-props/mdx-content-under-route.ts b/packages/xy-shared/server/mdx-content-under-route.ts similarity index 86% rename from sites/reactflow.dev/src/utils/get-static-props/mdx-content-under-route.ts rename to packages/xy-shared/server/mdx-content-under-route.ts index e5e21efb9..0b5e362df 100644 --- a/sites/reactflow.dev/src/utils/get-static-props/mdx-content-under-route.ts +++ b/packages/xy-shared/server/mdx-content-under-route.ts @@ -1,14 +1,22 @@ import { buildDynamicMDX } from 'nextra/remote'; import { readFile, readdir, stat } from 'fs/promises'; -import { type InternalRoute } from '../routes'; import * as Path from 'path'; import * as Url from 'url'; const __dirname = Url.fileURLToPath(import.meta.url); // @todo can we put this in xy-shared= -export async function getMdxContentUnderRoute(route: InternalRoute) { - const path = Path.join(__dirname, '../../../pages', route); +export async function getMdxContentUnderRoute( + site: string, + route: T, +) { + const path = Path.join( + __dirname, + '../../../../sites', + site, + 'src/pages', + route, + ); const files = await readdir(path); const mdx = []; diff --git a/packages/xy-shared/server/utils.ts b/packages/xy-shared/server/utils.ts new file mode 100644 index 000000000..50dc6bf7b --- /dev/null +++ b/packages/xy-shared/server/utils.ts @@ -0,0 +1,10 @@ +import { readFileSync } from 'fs'; + +export function loadJSONFile(url: string): T | undefined { + try { + const file = readFileSync(url, 'utf-8'); + return JSON.parse(file.toString()) as T; + } catch (err) { + console.log(err); + } +} diff --git a/packages/xy-shared/lib/globals.css b/packages/xy-shared/styles/globals.css similarity index 79% rename from packages/xy-shared/lib/globals.css rename to packages/xy-shared/styles/globals.css index 7e7f639a4..c2ef1a3f2 100644 --- a/packages/xy-shared/lib/globals.css +++ b/packages/xy-shared/styles/globals.css @@ -259,7 +259,21 @@ article.nextra-content ol li p { } .DocSearch-Button .DocSearch-Button-Keys { - font-size: 12px; + font-size: 14px; + min-width: 0; + margin-left: 10px; + margin-right: 5px; +} + +.DocSearch-Button .DocSearch-Button-Key { + background: transparent; + box-shadow: none; + top: 0; + width: auto; + height: auto; + padding: 0; + margin: 0; + margin-left: 4px; } .DocSearch-Button .DocSearch-Button-Placeholder { @@ -337,3 +351,70 @@ article.nextra-content ol li p { overflow-x: hidden; } } + +/* style of code blocks */ +.nextra-code > div:first-child { + @apply bg-primary/10; + border: none; +} + +.nextra-code pre { + @apply bg-primary/5; + box-shadow: none; +} + +/* needed for moving navbar elements to the left */ +.nextra-nav-container nav > div:first-child { + @apply !mr-5; +} + +/* stack example code and preview vertically */ +.sandpack-wrapper.vertical > .sp-wrapper > .sp-layout { + flex-direction: column-reverse; +} + +.sandpack-wrapper.vertical .sp-stack { + flex: none; +} + +/* We have this weird bug where the mobile top navbar can be scrolled out of view +even though the body is supposed to have overflow:hidden set. The following two +rules make it work almost correctly, but they don't feel great. */ + +html:has(body._overflow-hidden) { + overflow-y: hidden; +} + +html:has(body._overflow-hidden) nav { + position: fixed; + width: 100%; +} + +/* this override adds some space for the api docs */ + +.learn article main.md\:_px-12, +.api-reference article main.md\:_px-12, +.examples article main.md\:_px-12 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +/* this unifies the /examples content width with /api-refernce and /learn */ + +.examples main._max-w-6xl { + max-width: none; +} + +@media (max-width: 950px) { + .DocSearch-Button-Keys { + display: none; + } + + .DocSearch-Button-Placeholder { + display: none; + } + + .nextra-nav-container > nav { + @apply p-2 gap-2; + } +} diff --git a/packages/xy-shared/types/index.tsx b/packages/xy-shared/types/index.tsx new file mode 100644 index 000000000..8d321854d --- /dev/null +++ b/packages/xy-shared/types/index.tsx @@ -0,0 +1,11 @@ +export type ExampleCode = { + files: Record; + dependencies: Record; +}; + +export type CompiledMdx = { + compiledSource: string; + frontMatter: { + [key: string]: any; + }; +}; diff --git a/packages/xy-shared/widgets/about-section/index.tsx b/packages/xy-shared/widgets/about-section/index.tsx index 557b51af2..690ab2bce 100644 --- a/packages/xy-shared/widgets/about-section/index.tsx +++ b/packages/xy-shared/widgets/about-section/index.tsx @@ -16,7 +16,7 @@ function AboutSection({ imageSrc, colorizeImage = true }: AboutSectionProps) { A project by the xyflow team - We are Christopher, Hayleigh, Peter and Moritz. We are the maintainers + We are Christopher, Hayleigh, Peter, Abbey and Moritz. We are the maintainers of React Flow, Svelte Flow, and the communities around them diff --git a/packages/xy-shared/widgets/hero-flow/fiber.tsx b/packages/xy-shared/widgets/hero-flow/fiber.tsx index fb5429019..ca05488b7 100644 --- a/packages/xy-shared/widgets/hero-flow/fiber.tsx +++ b/packages/xy-shared/widgets/hero-flow/fiber.tsx @@ -44,7 +44,8 @@ function Shape({ type, random, color, ...props }: any) { ) : ( )} - + {/** @ts-ignore */} + ); } diff --git a/packages/xy-shared/widgets/image-slider/image-slider-item.tsx b/packages/xy-shared/widgets/image-slider/image-slider-item.tsx index d1fe44998..5a67ce901 100644 --- a/packages/xy-shared/widgets/image-slider/image-slider-item.tsx +++ b/packages/xy-shared/widgets/image-slider/image-slider-item.tsx @@ -21,7 +21,7 @@ export default function ImageSliderItem({ onClick(item.name)} diff --git a/packages/xy-shared/widgets/image-slider/image-slider-items.tsx b/packages/xy-shared/widgets/image-slider/image-slider-items.tsx index bfc676736..0628807b9 100644 --- a/packages/xy-shared/widgets/image-slider/image-slider-items.tsx +++ b/packages/xy-shared/widgets/image-slider/image-slider-items.tsx @@ -90,7 +90,7 @@ export default function ImageSliderItems({ ))}
- + {items.map((item, index) => ( { + files[file] = { + code: files[file], + hidden: true, + }; + }); + + return { files, dependencies: json.dependencies }; +} diff --git a/packages/xy-shared/widgets/remote-code-viewer/index.tsx b/packages/xy-shared/widgets/remote-code-viewer/index.tsx index 3d1f1c61d..950d637fc 100644 --- a/packages/xy-shared/widgets/remote-code-viewer/index.tsx +++ b/packages/xy-shared/widgets/remote-code-viewer/index.tsx @@ -1,20 +1,22 @@ -'use client'; +import { useContext } from 'react'; +import { clsx } from 'clsx'; +import { Code } from 'nextra/components'; import { - SandpackProvider, - SandpackLayout, - SandpackCodeEditor, - SandpackPreview, - SandpackStack, - OpenInCodeSandboxButton, - SandpackFiles, - SandpackPredefinedTemplate, -} from '@codesandbox/sandpack-react'; -import { Framework } from '@xyflow/xy-ui'; -import cn from 'clsx'; -import { useEffect, useState } from 'react'; -import { OpenInCodesandbox } from './open-in-codesandbox'; + Framework, + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from '@xyflow/xy-ui'; + +import { RemoteContent } from '../../components/remote-content'; +import { SharedContext } from '../../context/shared-context'; +import { CompiledMdx } from '../../types'; + +import './style.css'; import { OpenInStackblitz } from './open-in-stackblitz'; +import { OpenInCodesandbox } from './open-in-codesandbox'; const defaultOptions = { editorHeight: '60vh', @@ -24,187 +26,131 @@ const defaultOptions = { }; export type RemoteCodeViewerProps = { - source: RemoteCodeViewerSource; - preview: string; - framework: Framework; + route: string; + framework?: Framework; options?: typeof defaultOptions; activeFile?: string; showEditor?: boolean; showPreview?: boolean; - customOpenButton?: React.ReactNode; - sandpackOptions?: Record; showOpenInCodeSandbox?: boolean; editorHeight?: string | number; orientation?: 'horizontal' | 'vertical'; + sandpackOptions?: Record; }; -export type RemoteCodeViewerSource = - | string - | { - files: SandpackFiles; - dependencies: Record; - }; - export function RemoteCodeViewer({ - source, - preview, + route, framework, showEditor = true, - customOpenButton = null, - sandpackOptions = {}, showOpenInCodeSandbox = framework === 'react', + sandpackOptions = {}, editorHeight = '60vh', activeFile, orientation, }: RemoteCodeViewerProps) { - const [filesFetched, setFilesFetched] = useState(typeof source === 'string'); - const [fileFetchFailed, setFileFetchFailed] = useState(false); - const [files, setFiles] = useState( - typeof source === 'string' - ? { - 'index.html': ` - - - - - Example - - -
- -`, - } - : source.files, - ); - - const _orientation = orientation - ? orientation - : typeof source === 'string' && source.includes('/examples/') - ? 'vertical' - : 'horizontal'; - - const [dependencies, setDependencies] = useState>( - typeof source === 'string' ? {} : source.dependencies, - ); - - useEffect(() => { - const loadFiles = async (url: string) => { - const res = await fetch(url); - const json = await res.json(); - - setFilesFetched(true); - - if ('files' in json && 'dependencies' in json) { - const files = json.files; + const _framework: Framework = + framework ?? (process.env.NEXT_PUBLIC_Framework as Framework) ?? 'react'; - // this is a workaround for the examples that are using jsx - // if we don't do this, sandpack will generate a default App.tsx file - if (framework === 'react' && files['App.jsx']) { - files['App.tsx'] = files['App.jsx']; - delete files['App.jsx']; - } else if (framework === 'svelte') { - for (const file of Object.keys(files)) { - if (file === 'index.html') { - files[file] = files[file]?.replace('./index.ts', './src/main.ts'); - continue; - } + const preview = `${process.env.NEXT_PUBLIC_EXAMPLES_URL}/${_framework}/${route}/index.html`; - if (file === 'index.ts') { - files['src/main.ts'] = files[file]; - } else { - files[`src/${file}`] = files[file]; - } + const isExample = route.includes('examples/'); + const isHorizontal = orientation + ? orientation === 'horizontal' + : isExample + ? false + : true; - delete files[file]; - } - } + const { useData } = useContext(SharedContext); + const snippets: Record | undefined = + useData('codeSnippets')?.[route]; - // we want to hide these files in the editor on website to reduce the noise - ['index.html', 'index.jsx', 'index.tsx', 'src/main.ts'].forEach( - (file) => { - files[file] = { - code: files[file], - hidden: true, - }; - }, - ); - - setFiles(json.files); - setDependencies(json.dependencies); - } else { - setFileFetchFailed(true); - } - }; - - if (typeof source === 'string') { - loadFiles(source); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (fileFetchFailed) { - return ( -
-

Example failed to load

-
+ if (!snippets) { + throw new Error( + `Example code not found! Did you forget to call "export const getStaticProps = getStaticCode(["${route}"])" inside your route?`, ); } - const panelStyle = { height: editorHeight }; + if (isExample) { + delete snippets['index.html']; + delete snippets['index.jsx']; + } - // @ todo refactor this. activeFile should be passed separately or within the sandpackOptions - sandpackOptions.readOnly = true; - sandpackOptions.activeFile = sandpackOptions.activeFile || activeFile; + const _initialActiveFile = + activeFile ?? + (Object.keys(snippets).includes('App.jsx') + ? 'App.jsx' + : Object.keys(snippets)[0]); return (
- {filesFetched && ( - +