Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file added examples/public/focus-tray.riv
Binary file not shown.
Binary file added examples/public/key-and-text-listeners.riv
Binary file not shown.
Binary file added examples/public/multiline-text-input.riv
Binary file not shown.
Binary file added examples/public/text_input_semantics.riv
Binary file not shown.
19 changes: 19 additions & 0 deletions examples/src/components/DirectionalFocus.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react';

import DirectionalFocus from './DirectionalFocus';

const meta = {
title: 'Directional Focus',
component: DirectionalFocus,
parameters: {
layout: 'fullscreen',
},
args: {},
} satisfies Meta<typeof DirectionalFocus>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Grid: Story = {
name: 'Grid 1–9 (arrow keys)',
};
27 changes: 27 additions & 0 deletions examples/src/components/DirectionalFocus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { useRive } from '@rive-app/react-canvas';

const DirectionalFocus = () => {
const { RiveComponent } = useRive({
src: 'focus-tray.riv',
artboard: 'Grid',
stateMachine: 'State Machine 1',
autoBind: true,
autoplay: true,
tabIndex: 0,
});

return (
<div style={{ fontFamily: 'system-ui, sans-serif', padding: 24 }}>
<p style={{ maxWidth: 640, lineHeight: 1.5 }}>
Click the canvas or Tab into it, then use arrow keys to move focus
between the buttons; Enter activates.
</p>
<RiveComponent
style={{ width: 640, height: 420, border: '1px solid #ccc' }}
/>
</div>
);
};

export default DirectionalFocus;
30 changes: 30 additions & 0 deletions examples/src/components/TextInput.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';

import { LoginForm, MultilineAndListeners } from './TextInput';

const meta = {
title: 'Text Input',
component: LoginForm,
parameters: {
layout: 'fullscreen',
},
args: { semantics: true },
} satisfies Meta<typeof LoginForm>;

export default meta;
type Story = StoryObj<typeof meta>;

export const LoginFormSemantics: Story = {
name: 'Login form (semantics)',
};

export const LoginFormNoSemantics: Story = {
name: 'Login form (no semantics)',
args: { semantics: false },
};

export const MultilineAndListenersStory: Story = {
name: 'Multiline + listeners',
render: () => <MultilineAndListeners />,
};
82 changes: 82 additions & 0 deletions examples/src/components/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import { useRive, SemanticMode } from '@rive-app/react-canvas';

/**
* Text input fields and keyboard/text listeners. Rive handles keyboard input
* once its canvas is focused, so every canvas here gets `tabIndex: 0`.
*/

// Inline (not a shared CSSProperties) because RiveComponent's style type comes
// from the root package's @types/react.
const canvasStyle = (height: number) => ({
position: 'relative' as const,
width: 480,
height,
border: '1px solid #ccc',
margin: '12px 0',
});

const styles: Record<string, React.CSSProperties> = {
page: { fontFamily: 'system-ui, sans-serif', padding: 24 },
description: { maxWidth: 640, lineHeight: 1.5 },
row: { display: 'flex', flexWrap: 'wrap', gap: 16 },
};

// `position: relative` on the container is required for the semantics overlay.
const LoginFormCanvas = ({ semantics }: { semantics: boolean }) => {
const { RiveComponent } = useRive({
src: 'text_input_semantics.riv',
stateMachine: 'State Machine 1',
autoplay: true,
autoBind: true,
tabIndex: 0,
...(semantics ? { semanticsMode: SemanticMode.Enabled } : {}),
});
return <RiveComponent style={canvasStyle(360)} />;
};

export const LoginForm = ({ semantics }: { semantics: boolean }) => (
<div style={styles.page}>
<p style={styles.description}>
{semantics
? 'Semantics enabled: fields are mirrored by hidden DOM inputs, so screen readers and the browser Tab order see them. '
: 'Semantics disabled: the canvas is focusable and Rive handles focus between its own fields. '}
Click the Email field and type, try Backspace, Cmd/Ctrl+A and Cmd/Ctrl+C,
then Tab to Password (obscured) and on to the After button. Shift+Tab
walks back out through Before to the page button above the canvas.
</p>
<button type="button">Before</button>
<LoginFormCanvas semantics={semantics} />
<button type="button">After</button>
</div>
);

const ListenerCanvas = ({ src }: { src: string }) => {
const { RiveComponent } = useRive({
src,
artboard: 'Main',
stateMachine: 'State Machine 1',
autoplay: true,
autoBind: true,
tabIndex: 0,
});
return <RiveComponent style={canvasStyle(320)} />;
};

export const MultilineAndListeners = () => (
<div style={styles.page}>
<p style={styles.description}>
Left: a single-line Title and a multiline Message field, two-way bound to
view model readouts. Type in each; Enter inserts a newline in Message
only. Right: <strong>Both</strong> has keyboard and text listeners with
counters,
<strong>KeysOnly</strong> has keyboard listeners only, and{' '}
<strong>Field</strong> is a native text input. Click a node, type and
press keys to watch the counters.
</p>
<div style={styles.row}>
<ListenerCanvas src="multiline-text-input.riv" />
<ListenerCanvas src="key-and-text-listeners.riv" />
</div>
</div>
);
24 changes: 12 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
},
"homepage": "https://github.com/rive-app/rive-react#readme",
"dependencies": {
"@rive-app/canvas": "2.43.1",
"@rive-app/canvas-lite": "2.43.1",
"@rive-app/webgl2": "2.43.1"
"@rive-app/canvas": "2.44.0",
"@rive-app/canvas-lite": "2.44.0",
"@rive-app/webgl2": "2.44.0"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0"
Expand Down
Loading