diff --git a/codes/document.js b/codes/document.js new file mode 100644 index 0000000..950ea1a --- /dev/null +++ b/codes/document.js @@ -0,0 +1,47 @@ +const COMMON_CODE = ` +// You can use a key-value pair to share data with peers. +// The key is a string, and the value should be serializable - objects, arrays, and primitives. +doc.update((root) => { + root.num = 1; // {"num":1} + root.obj = {'str':'a'}; // {"num":1,"obj":{"str":"a"}} + root.arr = ['1', '2']; // {"num":1,"obj":{"str":"a"},"arr":[1,2]} +}); +`; + +const TEXT_CODE = ` +// Text provides supports for collaborative plain text editing. +// It also has selection information for sharing the cursor position. +doc.update((root) => { + root.text = new yorkie.Text(); // {"text":""} + root.text.edit(0, 0, 'hello'); // {"text":"hello"} + root.text.edit(0, 1, 'H'); // {"text":"Hello"} + root.text.select(0, 1); // {"text":"^H^ello"} +}); +`; + +const RICHTEXT_CODE = ` +// RichText is similar to Text except that we can add attributes to contents. +doc.update((root) => { + root.text = new yorkie.RichText(); // {"text":""} + root.text.edit(0, 0, 'hello'); // {"text":"hello"} + root.text.edit(0, 1, 'H'); // {"text":"Hello"} + root.text.setStyle(0, 1, {bold: true}); // {"text":"Hello"} +}); +`; + +const COUNTER_CODE = ` +// Counter supports numeric types that change with addition and subtraction. +doc.update((root) => { + root.counter = new yorkie.Counter(1); // {"counter":1} + root.counter.increase(2); // {"counter":3} + root.counter.increase(3.5); // {"counter":6.5} + root.counter.increase(-3.5); // {"counter":3} +}); +`; + +export const DOCUMENT_CODE = { + common: COMMON_CODE, + text: TEXT_CODE, + richText: RICHTEXT_CODE, + counter: COUNTER_CODE, +}; diff --git a/codes/features.js b/codes/features.js new file mode 100644 index 0000000..5b8ca70 --- /dev/null +++ b/codes/features.js @@ -0,0 +1,102 @@ +const PROFILE_JS = ` +import yorkie from 'yorkie-js-sdk'; + +async function main() { + const client = new yorkie.Client('${process.env.NEXT_PUBLIC_API_ADDR}', { + apiKey: 'MY_API_KEY', + // set the client's name and color to presence. + presence: { + name: getRandomName(), + color: getRandomColor(), + }, + }); + await client.activate(); + + const doc = new yorkie.Document('example-profile'); + await client.attach(doc); + + client.subscribe((event) => { + if (event.type === 'peers-changed') { + // get presence of all clients connected to the Document. + // {: {name: string, color: string}} + const peers = event.value[doc.getKey()]; + + // show peer list + updatePeerList(peers); + } + }); +} +main(); +`; + +const CURSOR_JS = ` +import yorkie from 'yorkie-js-sdk'; + +async function main() { + const client = new yorkie.Client('${process.env.NEXT_PUBLIC_API_ADDR}', { + apiKey: 'MY_API_KEY', + }); + await client.activate(); + + const doc = new yorkie.Document('my-first-document'); + await client.attach(doc); + + client.subscribe((event) => { + if (event.type === 'peers-changed') { + // get presence of all clients connected to the Document. + // {: {cursor: {x: number, y: number}}} + const peers = event.value[doc.getKey()]; + + // show peer cursors + updatePeerCursors(peers); + } + }); + + document.body.addEventListener('mousemove', (e) => { + // set the cursor position to presence. + // Presence will be shared with other clients. + client.updatePresence('cursor', { + x: e.clientX, + y: e.clientY, + }) + }); +} +main(); +`; + +const SELECTION_JS = ` +// js +`; + +const EDITING_JS = ` +// js +`; + +const EDITING_ANDROID = ` +// android +`; + +const EDITING_IOS = ` +// ios +`; + +export const FEATURES_CODE = { + profile: { + tabOrder: ['js'], + js: { title: 'JS SDK', code: PROFILE_JS, language: 'javascript' }, + }, + cursor: { + tabOrder: ['js'], + js: { title: 'JS SDK', code: CURSOR_JS, language: 'javascript' }, + }, + selection: { + tabOrder: ['js'], + js: { title: 'JS SDK', code: SELECTION_JS, language: 'javascript' }, + }, + editing: { + tabOrder: ['js', 'android', 'ios'], + js: { title: 'JS SDK', code: EDITING_JS, language: 'javascript' }, + android: { title: 'Android SDK', code: EDITING_ANDROID, language: 'kotlin' }, + ios: { title: 'iOS SDK', code: EDITING_IOS, language: 'swift' }, + }, +}; diff --git a/components/CodeBlock/PrismCode.tsx b/components/CodeBlock/PrismCode.tsx index be4720c..9ec1377 100644 --- a/components/CodeBlock/PrismCode.tsx +++ b/components/CodeBlock/PrismCode.tsx @@ -19,7 +19,7 @@ export type PrismCodeProps = { export function PrismCode({ code, language, withLineNumbers }: PrismCodeProps) { return ( - + {({ className, tokens, getLineProps, getTokenProps }) => (
           {tokens.map((line, i) => (
diff --git a/components/Layout/Header.tsx b/components/Layout/Header.tsx
index d7a4e73..231cab5 100644
--- a/components/Layout/Header.tsx
+++ b/components/Layout/Header.tsx
@@ -14,7 +14,7 @@ export function Header(): ReactElement {
   useEffect(() => {
     const isLoggedIn = isValidToken(localStorage.getItem('token'));
     setIsLoggedIn(isLoggedIn);
-  }, [setIsLoggedIn])
+  }, [setIsLoggedIn]);
 
   // TODO(hackerwins): Remove examples condition when examples are ready.
   return (
@@ -39,15 +39,13 @@ export function Header(): ReactElement {
                 Documentation
               
             
-            {
-              process.env.NODE_ENV === 'development' && (
-                
  • - - Examples - -
  • - ) - } + {process.env.NODE_ENV === 'development' && ( +
  • + + Examples + +
  • + )}
  • Community @@ -56,22 +54,25 @@ export function Header(): ReactElement {
    - { - isLoggedIn ? ( - - ) : ( - <> - - - - ) - } + {isLoggedIn ? ( + + ) : ( + <> + + + + )}
    diff --git a/components/Layout/MobileGnbDropdown.tsx b/components/Layout/MobileGnbDropdown.tsx index 212da95..a6495a6 100644 --- a/components/Layout/MobileGnbDropdown.tsx +++ b/components/Layout/MobileGnbDropdown.tsx @@ -4,9 +4,7 @@ import Link from 'next/link'; import classNames from 'classnames'; import { Popover, Icon } from 'components'; -export function MobileGnbDropdown({isLoggedIn} : { - isLoggedIn: boolean; -}) { +export function MobileGnbDropdown({ isLoggedIn }: { isLoggedIn: boolean }) { const [gnbOpened, setGnbOpened] = useState(false); const [docsMenuOpened, setDocsMenuOpened] = useState(false); const { asPath } = useRouter(); @@ -50,7 +48,7 @@ export function MobileGnbDropdown({isLoggedIn} : { setDocsMenuOpened((opened) => !opened); }} > - + Documentation
  • - { - process.env.NODE_ENV === 'development' && ( -
  • - - Example - -
  • - ) - } + {process.env.NODE_ENV === 'development' && ( +
  • + + Example + +
  • + )}
  • Dashboard @@ -179,7 +177,9 @@ export function MobileGnbDropdown({isLoggedIn} : {
  • Sign in @@ -187,7 +187,9 @@ export function MobileGnbDropdown({isLoggedIn} : {
  • Start for free diff --git a/pages/index.tsx b/pages/index.tsx index 336d7bd..817917a 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -2,7 +2,6 @@ import { useState } from 'react'; import classNames from 'classnames'; import type { NextPage } from 'next'; import Head from 'next/head'; -import Link from 'next/link'; import { Layout, Button, Icon, CodeBlock, CodeBlockHeader, Accordion } from '@/components'; import { ChartMotion, StateSharingMotion, ServerMotion, MainBannerMotion } from '@/components/motions'; import UserGroupSVG from '@/public/assets/icons/icon_service_main_users_group.svg'; @@ -10,31 +9,13 @@ import CollaboProfileSVG from '@/public/assets/icons/icon_collaborate_profile.sv import CollaboCursorSVG from '@/public/assets/icons/icon_collaborate_cursor.svg'; import CollaboSelectionSVG from '@/public/assets/icons/icon_collaborate_selection.svg'; import CollaboEditingSVG from '@/public/assets/icons/icon_collaborate_editing.svg'; +import { FEATURES_CODE } from '@/codes/features'; -type FeatureType = 'profile' | 'cursor' | 'selection' | 'editing'; -const sampleCode = `import yorkie from 'yorkie-js-sdk'; - -async function main() { - const client = new yorkie.Client('${process.env.NEXT_PUBLIC_API_ADDR}', { - apiKey: 'MY_API_KEY', - }); - await client.activate(); - - const doc = new yorkie.Document('my-first-document'); - await client.attach(doc); - - client.subscribe((event) => { - if (event.type === 'peers-changed') { - const peers = event.value[doc.getKey()]; - document.getElementById('peersCount').innerHTML = Object.entries(peers).length; - } - }); -} -main();`; - +type FeatureType = keyof typeof FEATURES_CODE; const Home: NextPage = () => { const [bannerActive, setBannerActive] = useState(false); const [activeFeatureCard, setActiveFeatureCard] = useState('profile'); + const [activeFeatureCode, setActiveFeatureCode] = useState({ type: 'js', info: FEATURES_CODE.profile.js }); // TODO(hackerwins): Remove examples condition when examples are ready. return ( @@ -89,7 +70,8 @@ const Home: NextPage = () => { collaborative in a flash!

    - Easily add collaboration to your apps with our API-based services.
    Sign up now and start building powerful, high-performance collaborative features in no time. + Easily add collaboration to your apps with our API-based services.
    Sign up now and start building + powerful, high-performance collaborative features in no time.

    + {FEATURES_CODE[activeFeatureCard].tabOrder.map((codeType) => ( + + ))} - + - + - { - process.env.NODE_ENV === 'development' && ( -
    -

    - What experiences
    - can you create -
    with Yorkie? -

    -

    Test our examples to add diverse collaborative features to your product.

    -
    -
    -
    -
    - - - -
    - ) - } + {process.env.NODE_ENV === 'development' && ( +
    +

    + What experiences
    + can you create +
    with Yorkie? +

    +

    Test our examples to add diverse collaborative features to your product.

    +
    +
    +
    +
    + + + +
    + )}

    Stable. @@ -226,10 +230,17 @@ const Home: NextPage = () => {
    Document and Presence

    - Document is stored using conflict-free replicated data types(CRDTs), which ensures that multiple users can edit the same data concurrently without encountering conflicts. - Presence represents a peer's awareness of the data being edited. It is used to track which users are currently editing the document. + Document is stored using conflict-free replicated data types(CRDTs), which ensures that multiple + users can edit the same data concurrently without encountering conflicts. Presence represents a + peer's awareness of the data being edited. It is used to track which users are currently + editing the document.

    -
    @@ -241,15 +252,11 @@ const Home: NextPage = () => {
    Data Warehouse with Dashboard

    - Dashboard allows users to easily browse stored documents and monitor the data warehouse in real-time. - With Dashboard, users can quickly and easily supervise the data warehouse and ensure that it is functioning properly. + Dashboard allows users to easily browse stored documents and monitor the data warehouse in + real-time. With Dashboard, users can quickly and easily supervise the data warehouse and ensure that + it is functioning properly.

    -
    @@ -261,8 +268,9 @@ const Home: NextPage = () => {
    Cloud or Self-Hosted Server

    - Yorkie offers flexible deployment options, allowing user to use a cloud or host the server on your own premises. - Whether you want the convenience of cloud or the control of a self-hosted server, Yorkie has you covered. + Yorkie offers flexible deployment options, allowing user to use a cloud or host the server on your + own premises. Whether you want the convenience of cloud or the control of a self-hosted server, + Yorkie has you covered.

    - diff --git a/pages/products.tsx b/pages/products.tsx index fc29bc3..a7f767e 100644 --- a/pages/products.tsx +++ b/pages/products.tsx @@ -1,6 +1,8 @@ +import { useState } from 'react'; import type { NextPage } from 'next'; import Head from 'next/head'; import Link from 'next/link'; +import classNames from 'classnames'; import { Button, Icon, Layout, CodeBlock, CodeBlockHeader } from '@/components'; import { StateSharingDetailMotion, FlexibleDocumentMotion } from '@/components/motions'; import ProductBannerSVG from '@/public/assets/images/banner/img_product_banner.svg'; @@ -9,17 +11,10 @@ import ProductAwarenessRightSVG from '@/public/assets/images/banner/img_product_ import ProductPCSVG from '@/public/assets/images/banner/img_product_pc.svg'; import ProductMobileSVG from '@/public/assets/images/banner/img_product_mobile.svg'; import ProductPackageSVG from '@/public/assets/images/banner/img_product_package.svg'; - -const textCode = `// Text provides supports for collaborative plain text editing. -// It also has selection information for sharing the cursor position. -doc.update((root) => { - root.text = new yorkie.Text(); // {"text":""} - root.text.edit(0, 0, 'hello'); // {"text":"hello"} - root.text.edit(0, 1, 'H'); // {"text":"Hello"} - root.text.select(0, 1); // {"text":"^H^ello"} -});`; +import { DOCUMENT_CODE } from '@/codes/document'; const Products: NextPage = () => { + const [documentType, setDocumentType] = useState('common'); return ( @@ -39,7 +34,12 @@ const Products: NextPage = () => { as a document store.

    - @@ -59,8 +59,8 @@ const Products: NextPage = () => {

    - Yorkie allow you to build multiplayer products without the need for database configuration - and conflict management. This saves time and money. + Yorkie allow you to build multiplayer products without the need for database configuration and conflict + management. This saves time and money.

    @@ -69,8 +69,9 @@ const Products: NextPage = () => {

    Yorkie implements real-time collaboration based on the Conflict-free Replicated Data Type(CRDT) algorithm. - CRDTs offer a clean and reliable way to resolve conflicts when editing concurrent data, unlike Operational Transformation(OT) algorithms - which can be complex and may not always ensure convergence. Yorkie's use of the well-proven CRDT algorithm ensures reliable services. + CRDTs offer a clean and reliable way to resolve conflicts when editing concurrent data, unlike Operational + Transformation(OT) algorithms which can be complex and may not always ensure convergence. Yorkie's + use of the well-proven CRDT algorithm ensures reliable services.

    @@ -81,30 +82,55 @@ const Products: NextPage = () => { Document

    - Yorkie provides a general-purpose JSON-like document to enable complex application models while some CRDT libraries that only offer basic data types. + Yorkie provides a general-purpose JSON-like document to enable + complex application models while some CRDT libraries that only offer basic data types.

    - - - - - + - +
    @@ -116,7 +142,8 @@ const Products: NextPage = () => { Presence

    - You can build a sense of presence by tracking the status of users who are editing the same document with Presence. + You can build a sense of presence by tracking the status of users who are editing the same document with + Presence.

    @@ -135,19 +162,23 @@ const Products: NextPage = () => {
  • SDKs for Mobile & Web

    - Yorkie SDKs support development for iOS, Android and Web applications. + Yorkie SDKs support development for iOS,{' '} + Android and Web applications.

  • Size optimization

    - Yorkie uses Garbage Collection and Lamport timestamps to reduce the size of documents. + Yorkie uses Garbage Collection and + Lamport timestamps to reduce the size of + documents.

  • Security

    - Auth Webhook allows users to verify the authorization of clients to access documents from an external service. + Auth Webhook allows users to verify the authorization + of clients to access documents from an external service.

  • @@ -168,7 +199,9 @@ const Products: NextPage = () => { Dashboard -

    Dashboard in Cloud is accessible from any device without the need for installation.

    +

    + Dashboard in Cloud is accessible from any device without the need for installation. +

    @@ -203,7 +236,8 @@ const Products: NextPage = () => { How to build self-hosted server

    - Yorkie open-source package includes SDKs, a server, and a database, making it easy to implement the co-editing feature. + Yorkie open-source package includes SDKs, a server, and a database, making it easy to implement the + co-editing feature.