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';