Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5311d16
feat(joint-react): sync with react plus and general fixes + remove pa…
samuelgja Oct 15, 2025
fdfff64
chore(joint-react): enhance graph provider and store with new methods…
samuelgja Oct 15, 2025
273e570
refactor(joint-react): clean up code by removing unused methods and s…
samuelgja Oct 15, 2025
751a252
Merge remote-tracking branch 'upstream/master' into feat/joint-react-…
samuelgja Nov 20, 2025
8896f66
chore(joint-react): sync yarn lock + update tests snapshots
samuelgja Nov 20, 2025
ce40cbb
feat(joint-react): add use-combined-ref hook export
samuelgja Nov 21, 2025
5ef0956
feat(joint-react): improve size measurement accuracy and prevent floa…
samuelgja Nov 21, 2025
29d9a17
feat(joint-react): enhance Paper component with improved dimension ha…
samuelgja Nov 25, 2025
8b035ed
Merge remote-tracking branch 'upstream/master' into feat/joint-react-…
samuelgja Nov 25, 2025
0321376
feat(joint-react): update dependencies, enhance GraphProvider, and ad…
samuelgja Nov 26, 2025
0fe069f
feat(joint-react): enhance GraphProvider for controlled mode and add …
samuelgja Nov 26, 2025
8d9dbc0
feat(joint-react): update dependencies, refactor components, and enha…
samuelgja Dec 9, 2025
32c1254
chore(joint-react): add empty lines to test files for consistency
samuelgja Dec 15, 2025
be7367a
feat(joint-react): - work only with json -remove dia.element and dia…
samuelgja Dec 18, 2025
efb9988
chore(joint-react): add empty lines to multiple test files for consis…
samuelgja Jan 6, 2026
6f76677
feat(joint-react): enhance useNodeSize hook with transform functionality
samuelgja Jan 9, 2026
a0f940b
feat(joint-react): improve size observer accuracy and performance
samuelgja Jan 9, 2026
86393e8
fix(joint-react): refine size observer logic and update EPSILON value
samuelgja Jan 9, 2026
5d51044
fix(joint-react): update paper component opacity and enhance size obs…
samuelgja Jan 14, 2026
ee2c6d3
feat(joint-react): enhance App component with detailed documentation …
samuelgja Jan 16, 2026
b7b462b
feat(joint-react): introduce useNodeLayout hook for node geometry ret…
samuelgja Jan 16, 2026
23c3130
refactor(joint-react): streamline element rendering and enhance node …
samuelgja Jan 16, 2026
351d9c1
feat(joint-react): introduce BaseLink and LinkLabel components for cu…
samuelgja Jan 17, 2026
9b03164
fix(joint-react): enhance element rendering with areElementsMeasured …
samuelgja Jan 17, 2026
6a1cd17
chore(joint-react): remove createElements and createLinks utilities, …
samuelgja Jan 23, 2026
f6edd1d
Merge remote-tracking branch 'upstream/dev' into feat/joint-react-plu…
samuelgja Jan 23, 2026
25bf1ce
chore(joint-react): remove TypeScript error suppression for Vite plugins
samuelgja Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
359 changes: 291 additions & 68 deletions examples/joint-react/src/App.tsx

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions packages/joint-core/types/joint.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2584,17 +2584,17 @@ export namespace dia {

export namespace highlighters {

import HighlighterView = dia.HighlighterView;
type HighlighterView = dia.HighlighterView;

interface AddClassHighlighterArguments extends HighlighterView.Options {
interface AddClassHighlighterArguments extends dia.HighlighterView.Options {
className?: string;
}

interface OpacityHighlighterArguments extends HighlighterView.Options {
interface OpacityHighlighterArguments extends dia.HighlighterView.Options {
alphaValue?: number;
}

interface StrokeHighlighterArguments extends HighlighterView.Options {
interface StrokeHighlighterArguments extends dia.HighlighterView.Options {
padding?: number;
rx?: number;
ry?: number;
Expand All @@ -2603,7 +2603,7 @@ export namespace highlighters {
attrs?: attributes.NativeSVGAttributes;
}

interface MaskHighlighterArguments extends HighlighterView.Options {
interface MaskHighlighterArguments extends dia.HighlighterView.Options {
padding?: number;
maskClip?: number;
deep?: boolean;
Expand Down Expand Up @@ -4507,8 +4507,8 @@ export namespace attributes {
filter?: string | dia.SVGFilterJSON;
fill?: string | dia.SVGPatternJSON | dia.SVGGradientJSON;
stroke?: string | dia.SVGPatternJSON | dia.SVGGradientJSON;
sourceMarker?: dia.SVGMarkerJSON;
targetMarker?: dia.SVGMarkerJSON;
sourceMarker?: dia.SVGMarkerJSON | null;
targetMarker?: dia.SVGMarkerJSON | null;
vertexMarker?: dia.SVGMarkerJSON;
props?: SVGAttributeProps;
text?: string;
Expand Down
77 changes: 47 additions & 30 deletions packages/joint-react/.storybook/decorators/with-simple-data.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-perf/jsx-no-new-object-as-prop */
//@ts-expect-error its js package without types
import JsonViewer from '@andypf/json-viewer/dist/esm/react/JsonViewer';

import type { HTMLProps, JSX, PropsWithChildren } from 'react';
import type { InferElement } from '@joint/react';
import {
createElements,
createLinks,
GraphProvider,
MeasuredNode,
Paper,
useElement,
} from '@joint/react';
// @ts-expect-error do not provide typings.
import JsonViewer from '@andypf/json-viewer/dist/esm/react/JsonViewer';
import { useCallback, useRef, type HTMLProps, type JSX, type PropsWithChildren } from 'react';
import { GraphProvider, useNodeSize, type GraphLink } from '@joint/react';
import { PAPER_CLASSNAME, PRIMARY } from '../theme';
import type { PartialStoryFn, StoryContext } from 'storybook/internal/types';
import { Paper } from '../../src/components/paper/paper';

export type StoryFunction = PartialStoryFn<any, any>;
export type StoryCtx = StoryContext<any, any>;

const initialElements = createElements([
export const testElements = [
{
id: '1',
label: 'Node 1',
Expand All @@ -24,6 +21,8 @@ const initialElements = createElements([
y: 20,
width: 150,
height: 50,
hoverColor: 'red',
angle: 0,
},
{
id: '2',
Expand All @@ -33,11 +32,13 @@ const initialElements = createElements([
y: 250,
width: 150,
height: 50,
hoverColor: 'blue',
angle: 0,
},
]);
];

export type SimpleElement = InferElement<typeof initialElements>;
const initialLinks = createLinks([
export type SimpleElement = (typeof testElements)[number];
export const testLinks: GraphLink[] = [
{
id: 'l-1',
source: '1',
Expand All @@ -48,27 +49,28 @@ const initialLinks = createLinks([
},
},
},
]);
];

export function SimpleGraphProviderDecorator({ children }: Readonly<PropsWithChildren>) {
return (
<GraphProvider initialElements={initialElements} initialLinks={initialLinks}>
<GraphProvider elements={testElements} links={testLinks}>
{children}
</GraphProvider>
);
}

export function SimpleGraphDecorator(Story: any) {
export function SimpleGraphDecorator(Story: StoryFunction, { args }: StoryCtx) {
return (
<SimpleGraphProviderDecorator>
<Story />
<Story {...args} />
</SimpleGraphProviderDecorator>
);
}

export function RenderItemDecorator(
properties: Readonly<{
renderElement: (element: SimpleElement) => JSX.Element;
renderLink?: (link: any) => JSX.Element;
}>
) {
return (
Expand All @@ -79,19 +81,20 @@ export function RenderItemDecorator(
height={450}
className={PAPER_CLASSNAME}
renderElement={properties.renderElement}
renderLink={properties.renderLink}
linkPinning={false}
/>
</SimpleGraphProviderDecorator>
</div>
);
}

function RenderSimpleRectElement(properties: SimpleElement) {
function RenderSimpleRectElement(properties: Readonly<SimpleElement>) {
const { width, color, height } = properties;
return <rect width={width} height={height} fill={color} />;
}

export function RenderPaperWithChildren(properties: Readonly<{ children: JSX.Element }>) {
export function RenderGraphViewWithChildren(properties: Readonly<{ children: JSX.Element }>) {
return (
<div style={{ width: '100%', height: 350 }}>
<SimpleGraphProviderDecorator>
Expand All @@ -108,21 +111,35 @@ export function RenderPaperWithChildren(properties: Readonly<{ children: JSX.Ele
);
}

export function SimpleRenderItemDecorator(Story: any) {
return <RenderItemDecorator renderElement={Story} />;
export function SimpleRenderItemDecorator(Story: StoryFunction, { args }: StoryCtx) {
const component = useCallback(
(element: SimpleElement) => <Story {...element} {...args} />,
[Story, args]
);
return <RenderItemDecorator renderElement={component} />;
}

export function SimpleRenderPaperDecorator(Story: any) {
return <RenderPaperWithChildren>{Story}</RenderPaperWithChildren>;
export function SimpleRenderLinkDecorator(Story: StoryFunction, { args }: StoryCtx) {
const component = useCallback(
(element: SimpleElement) => <Story {...element} {...args} />,
[Story, args]
);
return (
<RenderItemDecorator
renderLink={component}
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
renderElement={({ id }) => <HTMLNode className="node">{id}</HTMLNode>}
/>
);
}

export function HTMLNode(props: PropsWithChildren<HTMLProps<HTMLDivElement>>) {
const { width, height } = useElement();
const elementRef = useRef<HTMLDivElement>(null);
const { width, height } = useNodeSize(elementRef);

return (
<foreignObject width={width} height={height} overflow="visible">
<MeasuredNode>
<div {...props} />
</MeasuredNode>
<div ref={elementRef} {...props} />
</foreignObject>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import React from 'react';

export function withStrictMode(Story: any) {
return (
<React.StrictMode>
<Story />
</React.StrictMode>
// <React.StrictMode>
<Story />
// </React.StrictMode>
);
}
25 changes: 23 additions & 2 deletions packages/joint-react/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/* eslint-disable @typescript-eslint/no-shadow */
/* eslint-disable no-shadow */
import type { StorybookConfig } from '@storybook/react-vite';
import path from 'node:path';
import { configureSort } from 'storybook-multilevel-sort';

configureSort({
Expand All @@ -23,8 +26,6 @@ const config: StorybookConfig = {
'@storybook/addon-interactions',
'@storybook/addon-docs',
'@storybook/addon-a11y',
// TODO: this library is not compatible with Vite storybook, so we will wait to fix it and then we can again enable.
// '@storybook/addon-storysource',
'@storybook/addon-links',
'storybook-addon-performance',
'@codesandbox/storybook-addon',
Expand All @@ -37,5 +38,25 @@ const config: StorybookConfig = {
docs: {
autodocs: true,
},
tags: {
// Custom tags for organizing stories
component: {},
hook: {},
example: {},
demo: {},
tutorial: {},
utils: {},
},

// 👇 extend Vite config here to resolve libraries properly (in storybook)
viteFinal: async (config) => {
config.resolve = config.resolve || {};
config.resolve.alias = {
...config.resolve.alias,
'@joint/react': path.resolve(__dirname, '../src/index.ts'),
'@joint/react/src/*': path.resolve(__dirname, '../src/*'),
};
return config;
},
};
export default config;
55 changes: 55 additions & 0 deletions packages/joint-react/.storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
<!-- Loads a font from a CDN -->
<!-- react-scan: Load with delay to avoid freezing Storybook -->
<script>
// Load react-scan UI overlay after Storybook initializes
(function () {
if (typeof window === 'undefined') return;

// Wait for DOM and React to be ready
function loadReactScan() {
if (window.__REACT_SCAN_LOADED) return;

const script = document.createElement('script');
script.src = 'https://unpkg.com/react-scan@latest/dist/auto.global.js';
script.async = true;
script.onload = function () {
window.__REACT_SCAN_LOADED = true;


// Wait a bit for react-scan to initialize, then configure it for iframe
setTimeout(function () {
// Access react-scan global API
const reactScan = window.reactScan;

if (reactScan && typeof reactScan === 'function') {
// Configure react-scan to work in iframe (Storybook uses iframes)
try {
reactScan({
enabled: false, // Disabled by default - user can enable via toolbar
allowInIframe: true,
showToolbar: true, // Show toolbar so user can toggle it on/off
});

} catch (error) {
console.error('❌ Error configuring react-scan:', error);
}
} else {
console.warn('⚠️ react-scan function not found');
}
}, 500);
};
script.onerror = function () {
console.error('❌ Failed to load react-scan');
};
document.head.appendChild(script);
}

// Try loading after a delay
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
setTimeout(loadReactScan, 3000);
});
} else {
setTimeout(loadReactScan, 3000);
}
})();
</script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
1 change: 0 additions & 1 deletion packages/joint-react/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import './wdyr';
import type { Preview } from '@storybook/react';
import { withPerformance } from 'storybook-addon-performance';
import { theme } from './theme';
Expand Down
7 changes: 0 additions & 7 deletions packages/joint-react/.storybook/wdyr.ts

This file was deleted.

Loading