diff --git a/demo-react/README.md b/demo-react/README.md
index ad388f25..1ebe379f 100644
--- a/demo-react/README.md
+++ b/demo-react/README.md
@@ -1,5 +1,3 @@
-[] texts on the slide
-
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
diff --git a/demo-react/public/alignment.svg b/demo-react/public/alignment.svg
new file mode 100644
index 00000000..e9855335
--- /dev/null
+++ b/demo-react/public/alignment.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo-react/public/bubble-diagram.svg b/demo-react/public/bubble-diagram.svg
new file mode 100644
index 00000000..87ab644a
--- /dev/null
+++ b/demo-react/public/bubble-diagram.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo-react/public/collaborative-document.svg b/demo-react/public/collaborative-document.svg
new file mode 100644
index 00000000..5d9cdce6
--- /dev/null
+++ b/demo-react/public/collaborative-document.svg
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo-react/public/contrast.svg b/demo-react/public/contrast.svg
new file mode 100644
index 00000000..4a2915b1
--- /dev/null
+++ b/demo-react/public/contrast.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo-react/public/proximity.svg b/demo-react/public/proximity.svg
new file mode 100644
index 00000000..e34565ae
--- /dev/null
+++ b/demo-react/public/proximity.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo-react/public/repetition.svg b/demo-react/public/repetition.svg
new file mode 100644
index 00000000..b6b9fbfc
--- /dev/null
+++ b/demo-react/public/repetition.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo-react/src/App.tsx b/demo-react/src/App.tsx
index c9484437..f022a3fd 100644
--- a/demo-react/src/App.tsx
+++ b/demo-react/src/App.tsx
@@ -1,6 +1,17 @@
import { useContext, useEffect } from 'react';
+import { nanoid } from 'nanoid';
-import { ComingSoon, CommentDrawer, Header, SlideMenu, SpacesContext } from './components';
+import {
+ ComingSoon,
+ CommentDrawer,
+ Header,
+ SlideMenu,
+ SpacesContext,
+ Title,
+ Paragraph,
+ Image,
+ CurrentSlide,
+} from './components';
import { getRandomName, getRandomColor } from './utils';
import { useMembers } from './hooks';
@@ -44,16 +55,13 @@ const App = () => {
id="slide-selected"
className="shadow-ably-paper xs:m-4 md:m-0 md:relative md:w-[1020px] md:h-[687px] md:min-w-[1020px] md:min-h-[687px] md:top-[79px] lg:mr-[380px]"
>
-
+
+ {/* TODO: cursor container component */}
-
@@ -66,12 +74,169 @@ export default App;
const slides = [
{
- children: <>Slide 1>,
+ children: (
+
+
+
+ Key Design Principles
+
+
+ Effective design centres on four basic principles: contrast, repetition, alignment and proximity. These
+ appear in every design.
+
+
+
+
+
+
+ Contrast
+
+
+ When a design uses several elements, the goal is to make each one distinct.
+
+
+
+
+
+
+ Repetition
+
+
+ Repetition helps designers establish relationships, develop organization and strengthen unity.
+
+
+
+
+
+
+ Alignment
+
+
+ Alignment creates a clean, sophisticated look. All elements should relate to all others in some way.
+
+
+
+
+
+
+ Proximity
+
+
+ When items are grouped, they become a single visual unit, rather than several separate entities.
+
+
+
+
+
+ ),
},
{
- children: <>Slide 2>,
+ children: (
+
+
+
+ How users read
+
+
+ Add graphics
+
+
+ No one likes boring text blocks on a website. And{' '}
+ images and icons {' '}
+ are the fastest way to get information.
+
+
+ But don't overdo it
+ . If you can't explain for what purpose you put this line or icon, it's better to abandon it.
+
+
+
+
+ ),
},
{
- children: <>Slide 3>,
+ children: (
+
+
+
+ Design Statistics
+
+
+ How do SMBs rate the importance of graphic design to their success?
+
+
+
+
+ ),
},
];
diff --git a/demo-react/src/components/Avatar.tsx b/demo-react/src/components/Avatar.tsx
index 674a43b8..1d1a062b 100644
--- a/demo-react/src/components/Avatar.tsx
+++ b/demo-react/src/components/Avatar.tsx
@@ -27,7 +27,8 @@ export const Avatar = ({
'rounded-full group relative flex items-center justify-center xs:h-[32px] xs:w-[32px] md:h-[46px] md:w-[46px]',
{
'bg-gradient-to-b from-yellow-400 to-yellow-500 ': isSelf,
- 'bg-white': !isSelf,
+ 'bg-white': !isSelf && !isInContent,
+ 'bg-[#F7F6F9]': isInContent,
},
)}
data-id="avatar-wrapper"
diff --git a/demo-react/src/components/AvatarStack.tsx b/demo-react/src/components/AvatarStack.tsx
index e5ad2491..357f6d4e 100644
--- a/demo-react/src/components/AvatarStack.tsx
+++ b/demo-react/src/components/AvatarStack.tsx
@@ -33,7 +33,13 @@ export const AvatarStack = ({ isInContent = false, avatars }: Props) => {
{hiddenAvatars.length > 0 && (
[];
+}
+
+export const CurrentSlide = ({ slides }: Props) => {
+ const { self } = useMembers();
+ const slide = self?.location?.slide || 0;
+ return (
+
+ {slides[slide].children}
+
+ );
+};
diff --git a/demo-react/src/components/Image.tsx b/demo-react/src/components/Image.tsx
new file mode 100644
index 00000000..0c402cf3
--- /dev/null
+++ b/demo-react/src/components/Image.tsx
@@ -0,0 +1,35 @@
+import cn from 'classnames';
+import { useElementSelect, useMembers } from '../hooks';
+import { findActiveMember, getMemberFirstName, getOutlineClasses } from '../utils';
+
+interface Props {
+ src: string;
+ children?: React.ReactNode;
+ className?: string;
+ id?: string;
+}
+
+export const Image = ({ src, children, className, id }: Props) => {
+ const { members } = useMembers();
+ const { handleSelect } = useElementSelect(id);
+ const activeMember = findActiveMember(id, members);
+ const name = getMemberFirstName(activeMember);
+ const outlineClasses = getOutlineClasses(activeMember);
+
+ return (
+
+
+ {children ? children : null}
+
+ );
+};
diff --git a/demo-react/src/components/Paragraph.tsx b/demo-react/src/components/Paragraph.tsx
index eabe96c7..89fba2a4 100644
--- a/demo-react/src/components/Paragraph.tsx
+++ b/demo-react/src/components/Paragraph.tsx
@@ -1,17 +1,34 @@
import cn from 'classnames';
+import { useElementSelect, useMembers } from '../hooks';
+import { findActiveMember, getMemberFirstName, getOutlineClasses } from '../utils';
interface Props extends React.HTMLAttributes
{
variant?: 'regular' | 'aside';
}
-export const Paragraph = ({ variant = 'regular', ...props }: Props) => {
+export const Paragraph = ({ variant = 'regular', id, className, ...props }: Props) => {
+ const { members } = useMembers();
+ const { handleSelect } = useElementSelect(id);
+ const activeMember = findActiveMember(id, members);
+ const name = getMemberFirstName(activeMember);
+ const outlineClasses = getOutlineClasses(activeMember);
+
return (
);
};
diff --git a/demo-react/src/components/SlidePreview.tsx b/demo-react/src/components/SlidePreview.tsx
index 95e24c2b..bf5969a1 100644
--- a/demo-react/src/components/SlidePreview.tsx
+++ b/demo-react/src/components/SlidePreview.tsx
@@ -4,12 +4,12 @@ import { useContext } from 'react';
import { SpacesContext } from '../components';
import { useMembers } from '../hooks';
-interface Props {
+export interface SlidePreviewProps {
children: React.ReactNode;
index: number;
}
-export const SlidePreview = ({ children, index }: Props) => {
+export const SlidePreview = ({ children, index }: SlidePreviewProps) => {
const space = useContext(SpacesContext);
const { self, members } = useMembers();
const membersOnASlide = (members || []).filter(({ location }) => location?.slide === index);
diff --git a/demo-react/src/components/Title.tsx b/demo-react/src/components/Title.tsx
index bc762cc1..f9933090 100644
--- a/demo-react/src/components/Title.tsx
+++ b/demo-react/src/components/Title.tsx
@@ -1,23 +1,37 @@
import cn from 'classnames';
+import { useElementSelect, useMembers } from '../hooks';
+import { findActiveMember, getMemberFirstName, getOutlineClasses } from '../utils';
interface Props extends React.HTMLAttributes {
variant?: 'h1' | 'h2' | 'h3';
}
-export const Title = ({ variant = 'h1', ...props }: Props) => {
+export const Title = ({ variant = 'h1', className, id, ...props }: Props) => {
const Component = variant;
+ const { members } = useMembers();
+ const { handleSelect } = useElementSelect(id);
+ const activeMember = findActiveMember(id, members);
+ const name = getMemberFirstName(activeMember);
+ const outlineClasses = getOutlineClasses(activeMember);
+
return (
);
};
diff --git a/demo-react/src/components/index.ts b/demo-react/src/components/index.ts
index b1d4e4e0..63974621 100644
--- a/demo-react/src/components/index.ts
+++ b/demo-react/src/components/index.ts
@@ -4,7 +4,10 @@ export * from './AvatarStack';
export * from './ComingSoon';
export * from './Comment';
export * from './CommentDrawer';
+export * from './CurrentSlide';
export * from './Header';
+export * from './Image';
+export * from './Paragraph';
export * from './SlideMenu';
export * from './SlidePreview';
export * from './SpacesContext';
diff --git a/demo-react/src/hooks/index.ts b/demo-react/src/hooks/index.ts
index a8eb6ed9..b89a3baf 100644
--- a/demo-react/src/hooks/index.ts
+++ b/demo-react/src/hooks/index.ts
@@ -1 +1,2 @@
export * from './useMembers';
+export * from './useElementSelect';
diff --git a/demo-react/src/hooks/useElementSelect.ts b/demo-react/src/hooks/useElementSelect.ts
new file mode 100644
index 00000000..ff6c7ac4
--- /dev/null
+++ b/demo-react/src/hooks/useElementSelect.ts
@@ -0,0 +1,15 @@
+import { useContext } from 'react';
+import { SpacesContext } from '../components';
+import { useMembers } from './useMembers';
+
+export const useElementSelect = (element?: string) => {
+ const space = useContext(SpacesContext);
+ const { self } = useMembers();
+
+ const handleSelect = () => {
+ if (!space || !self) return;
+ space.locations.set({ slide: self.location?.slide, element });
+ };
+
+ return { handleSelect };
+};
diff --git a/demo-react/src/utils/active-member.ts b/demo-react/src/utils/active-member.ts
new file mode 100644
index 00000000..ffe65205
--- /dev/null
+++ b/demo-react/src/utils/active-member.ts
@@ -0,0 +1,19 @@
+import { SpaceMember } from '@ably-labs/spaces';
+
+export const findActiveMember = (id?: string, members?: SpaceMember[]) => {
+ if (!members) return;
+ return members.find((member) => member.location?.element === id);
+};
+
+export const getMemberFirstName = (member?: SpaceMember) => {
+ if (!member) return '';
+ return member.profileData.name.split(' ')[0];
+};
+
+export const getOutlineClasses = (member?: SpaceMember) => {
+ if (!member) return '';
+ const { color } = member.profileData;
+ const { name } = color;
+ const { intensity } = color.gradientStart;
+ return `outline-${name}-${intensity} before:bg-${name}-${intensity}`;
+};
diff --git a/demo-react/src/utils/index.ts b/demo-react/src/utils/index.ts
index d2df2299..1fa75631 100644
--- a/demo-react/src/utils/index.ts
+++ b/demo-react/src/utils/index.ts
@@ -1,4 +1,5 @@
export * from './colors';
export * from './fake-names';
+export * from './active-member';
export * from './spaces';
export * from './url';