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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 23 additions & 15 deletions apps/docs/src/pages/en/website/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ layout: ../../../layouts/MainLayout.astro

Every page in CourseLit is made up of various blocks, stacked in a top-to-bottom fashion. Each block serves a unique purpose and can be customized.

The following screenshot shows [Header](/en/pages/header), [Rich Text](/en/pages/banner), [Hero](/en/pages/content), and [Grid](/en/pages/grid) blocks (top to bottom) in action. Different blocks are highlighted in different colors.
The following screenshot shows [Header](/en/website/blocks#header), [Rich Text](/en/website/blocks#rich-text), [Hero](/en/website/blocks#hero), and [Grid](/en/website/blocks#grid) blocks (top to bottom) in action. Different blocks are highlighted in different colors.

![CourseLit page blocks](/assets/pages/page-builder-blocks.png)

Expand Down Expand Up @@ -36,32 +36,40 @@ You will also see the newly added link on the header itself.
3. Click on the pencil icon against the newly added link to edit it as shown above.
4. Change the label (displayed as text on the header block) and the URL (where the user should be taken upon clicking the label on the header) and click `Done` to save.
![Header edit link](/assets/pages/header-edit-link.png)
</details>
</details>

### [Rich Text](#rich-text)

<details>
<summary>Expand to see Rich Text block details</summary>

The rich text block can be used to add text blocks containing elements like hyperlinks, etc.
The rich text block uses the same text editor available elsewhere on the platform. It supports all functionality that does not require a toolbar, as the toolbar is hidden in this block.

#### Making text bold/italic/underline
#### Keyboard shortcuts

1. Select the text.
2. To make the selected text bold, press <kbd>Ctrl+B</kbd>; to make it italic, press <kbd>Ctrl+I</kbd>; and for underline, press <kbd>Ctrl+U</kbd>.

You can also use the floating controls to do the same as shown below.
2. Use the following shortcuts to format it:

- **Bold**: <kbd>Ctrl+B</kbd>
- **Italic**: <kbd>Ctrl+I</kbd>
- **Underline**: <kbd>Ctrl+U</kbd>
- **Strikethrough**: <kbd>Ctrl+Shift+S</kbd>
- **Undo**: <kbd>Ctrl+Z</kbd>
- **Redo**: <kbd>Ctrl+Shift+Z</kbd>
- **Paste**: <kbd>Ctrl+V</kbd>
- **Ordered list**: <kbd>Ctrl+Shift+7</kbd>
- **Bulleted list**: <kbd>Ctrl+Shift+8</kbd>
- **Highlight**: <kbd>Ctrl+Shift+H</kbd> (or type `==two equal signs==`)

![Stylised text](/assets/pages/rich-text-styling.gif)

#### Creating hyperlinks

1. Select the text.
> Double-clicking the text to select won't work due to a bug. We are working on it.
2. Click on the floating `link` button to reveal a popup text input.
3. In the popup text input, enter the URL as shown below.
![Create a hyperlink in rich text block](/assets/pages/rich-text-create-hyperlink.gif)
</details>
2. Click on the floating `link` icon to reveal a text input.
3. In the popup text input, enter the URL as shown below and press <kbd>Enter</kbd>.
![Create a hyperlink in rich text block](/assets/pages/courselit-text-editor-create-links.gif)
</details>

### [Hero](#hero)

Expand All @@ -87,7 +95,7 @@ Following is how it looks on a page.
4. In the button action, enter the URL the user should be taken to upon clicking.
a. If the URL is from your own school, use its relative form, i.e., `/courses`.
b. If the URL is from some external website, use the absolute (complete) URL, i.e., `https://website.com/courses`.
</details>
</details>

### [Grid](#grid)

Expand Down Expand Up @@ -132,7 +140,7 @@ A grid block comes in handy when you want to show some sort of list, for example
4. In the button action, enter the URL the user should be taken to upon clicking.
a. If the URL is from your own school, use its relative form, i.e., `/courses`.
b. If the URL is from some external website, use the absolute (complete) URL, i.e., `https://website.com/courses`.
</details>
</details>

### [Featured](#featured)

Expand Down Expand Up @@ -268,7 +276,7 @@ In the `Design` panel, you can customize:
- Maximum width
- Vertical padding
- Social media links (Facebook, Twitter, Instagram, LinkedIn, YouTube, Discord, GitHub)
</details>
</details>

## [Shared blocks](#shared-blocks)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
"use client";

import { TextRenderer } from "@courselit/components-library";
import { TextRenderer } from "@courselit/page-blocks";
import { ThemeStyle } from "@courselit/page-models";
import { TableOfContent } from "@components/table-of-content";
import WidgetErrorBoundary from "@components/public/base-layout/template/widget-error-boundary";

export default function ClientSideTextRenderer({ json }: { json: any }) {
return <TextRenderer json={json} showTableOfContent={true} />;
export default function ClientSideTextRenderer({
json,
theme,
}: {
json: any;
theme: ThemeStyle;
}) {
return (
<div className="flex flex-col gap-4">
<TableOfContent json={json} theme={theme} />
<WidgetErrorBoundary widgetName="text-editor">
<TextRenderer json={json} theme={theme} />
</WidgetErrorBoundary>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Course } from "@courselit/common-models";
import { Caption, Header1, Section, Text1 } from "@courselit/page-primitives";
import { Caption, Header1, Section } from "@courselit/page-primitives";
import { formattedLocaleDate, getFullSiteSetup } from "@ui-lib/utils";
import { getAddressFromHeaders } from "@/app/actions";
import { headers } from "next/headers";
Expand Down Expand Up @@ -96,11 +96,10 @@ export default async function ProductPage(props: {
</div>
)}
{product?.description && (
<Text1 theme={theme.theme} component="span">
<ClientSideTextRenderer
json={JSON.parse(product.description)}
/>
</Text1>
<ClientSideTextRenderer
json={JSON.parse(product.description)}
theme={theme.theme}
/>
)}
</div>
</Section>
Expand Down
32 changes: 22 additions & 10 deletions apps/web/app/(with-contexts)/course/[slug]/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ import {
Link,
Button2,
getSymbolFromCurrency,
TextRenderer,
TextEditorEmptyDoc,
Image,
} from "@courselit/components-library";
import { TextRenderer } from "@courselit/page-blocks";
import { TableOfContent } from "@components/table-of-content";
import {
AddressContext,
ProfileContext,
SiteInfoContext,
ThemeContext,
} from "@components/contexts";
import { getProduct } from "./helpers";
import { getUserProfile } from "@/app/(with-contexts)/helpers";
import { BadgeCheck } from "lucide-react";
import { emptyDoc as TextEditorEmptyDoc } from "@courselit/text-editor";
import WidgetErrorBoundary from "@components/public/base-layout/template/widget-error-boundary";
const { permissions } = UIConstants;

export default function ProductPage(props: {
Expand All @@ -38,6 +41,7 @@ export default function ProductPage(props: {
const siteInfo = useContext(SiteInfoContext);
const address = useContext(AddressContext);
const [progress, setProgress] = useState<any>(null);
const { theme } = useContext(ThemeContext);

useEffect(() => {
if (id) {
Expand Down Expand Up @@ -68,6 +72,10 @@ export default function ProductPage(props: {
return null;
}

const descriptionJson = product.description
? JSON.parse(product.description)
: TextEditorEmptyDoc;

return (
<div className="flex flex-col pb-[100px] lg:max-w-[40rem] xl:max-w-[48rem] mx-auto">
<h1 className="text-4xl font-semibold mb-8">{product.title}</h1>
Expand Down Expand Up @@ -118,14 +126,18 @@ export default function ProductPage(props: {
</div>
)}
<div className="overflow-hidden min-h-[360px]">
<TextRenderer
json={
product.description
? JSON.parse(product.description)
: TextEditorEmptyDoc
}
showTableOfContent={true}
/>
<div className="flex flex-col gap-4">
<TableOfContent
json={descriptionJson}
theme={theme.theme}
/>
<WidgetErrorBoundary widgetName="text-editor">
<TextRenderer
json={descriptionJson}
theme={theme.theme}
/>
</WidgetErrorBoundary>
</div>
</div>
{isEnrolled(product.courseId, profile as Profile) && (
<div className="self-end">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
"use client";

import DashboardContent from "@components/admin/dashboard-content";
import {
AddressContext,
ProfileContext,
SiteInfoContext,
} from "@components/contexts";
import { AddressContext, ProfileContext } from "@components/contexts";
import {
COMMUNITY_HEADER,
COMMUNITY_SETTINGS,
Expand All @@ -31,8 +27,6 @@ import {
Image,
Link,
MediaSelector,
TextEditor,
TextEditorEmptyDoc,
useToast,
} from "@courselit/components-library";
import { Separator } from "@components/ui/separator";
Expand Down Expand Up @@ -72,6 +66,7 @@ import { Input } from "@/components/ui/input";
import { redirect, useRouter } from "next/navigation";
import { useMembership } from "@/hooks/use-membership";
import { useGraphQLFetch } from "@/hooks/use-graphql-fetch";
import { Editor, emptyDoc as TextEditorEmptyDoc } from "@courselit/text-editor";
const { PaymentPlanType: paymentPlanType, MembershipEntityType } = Constants;

export default function Page(props: {
Expand All @@ -90,7 +85,6 @@ export default function Page(props: {
];
const { profile } = useContext(ProfileContext);
const address = useContext(AddressContext);
const siteinfo = useContext(SiteInfoContext);

const [name, setName] = useState("");
const [enabled, setEnabled] = useState(false);
Expand Down Expand Up @@ -565,7 +559,7 @@ export default function Page(props: {
/>
<div>
<h2 className="font-semibold">Description</h2>
<TextEditor
<Editor
initialContent={description}
onChange={(state: any) => setDescription(state)}
showToolbar={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,7 @@ import {
TextEditorContent,
UIConstants,
} from "@courselit/common-models";
import {
MediaSelector,
TextEditor,
TextEditorEmptyDoc,
useToast,
} from "@courselit/components-library";
import { MediaSelector, useToast } from "@courselit/components-library";
import {
MIMETYPE_VIDEO,
MIMETYPE_AUDIO,
Expand All @@ -67,6 +62,7 @@ import { QuizBuilder } from "@components/admin/products/quiz-builder";
import { isTextEditorNonEmpty, truncate } from "@ui-lib/utils";
import { Skeleton } from "@/components/ui/skeleton";
import { Separator } from "@components/ui/separator";
import { Editor, emptyDoc as TextEditorEmptyDoc } from "@courselit/text-editor";

const { permissions } = UIConstants;

Expand Down Expand Up @@ -214,7 +210,7 @@ export default function LessonPage() {
case Constants.LessonType.TEXT:
return (
<div className="space-y-2">
<TextEditor
<Editor
initialContent={textContent}
refresh={refresh}
onChange={(state: any) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import {
TextEditor,
TextEditorEmptyDoc,
useToast,
} from "@courselit/components-library";
import { useToast } from "@courselit/components-library";
import { AddressContext } from "@components/contexts";
import {
APP_MESSAGE_COURSE_SAVED,
Expand All @@ -20,6 +16,7 @@ import {
} from "@ui-config/strings";
import { useGraphQLFetch } from "@/hooks/use-graphql-fetch";
import { Save, Loader2 } from "lucide-react";
import { Editor, emptyDoc as TextEditorEmptyDoc } from "@courselit/text-editor";

const MUTATION_UPDATE_BASIC_DETAILS = `
mutation UpdateBasicDetails($courseId: String!, $title: String!, $description: String!) {
Expand Down Expand Up @@ -131,7 +128,7 @@ export default function ProductDetails({ product }: ProductDetailsProps) {
Description
</Label>

<TextEditor
<Editor
initialContent={formData.description}
onChange={(state: any) => {
handleInputChange({
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "remirror/styles/all.css";
import "@courselit/page-blocks/styles.css";
import "@courselit/components-library/styles.css";
import "@courselit/page-primitives/styles.css";
import "@courselit/text-editor/styles.css";
import "../styles/globals.css";
import type { Metadata } from "next";
import { headers } from "next/headers";
Expand Down
12 changes: 9 additions & 3 deletions apps/web/components/admin/blogs/editor/details.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, { FormEvent, useContext, useEffect, useState } from "react";
import {
MediaSelector,
TextEditor,
TextEditorEmptyDoc,
Form,
FormField,
Button,
Expand All @@ -23,6 +21,7 @@ import {
import { MIMETYPE_IMAGE } from "@/ui-config/constants";
import { Media, Profile } from "@courselit/common-models";
import { AddressContext, ProfileContext } from "@components/contexts";
import { Editor, emptyDoc as TextEditorEmptyDoc } from "@courselit/text-editor";

interface DetailsProps {
id: string;
Expand Down Expand Up @@ -145,12 +144,19 @@ export default function Details({ id }: DetailsProps) {
onChange={(e) => setTitle(e.target.value)}
/>
<PageBuilderPropertyHeader label={COURSE_CONTENT_HEADER} />
<TextEditor
<Editor
initialContent={description}
refresh={refreshDetails}
onChange={(state: any) => setDescription(state)}
url={address.backend}
placeholder={TEXT_EDITOR_PLACEHOLDER}
onError={(err: any) => {
toast({
title: TOAST_TITLE_ERROR,
description: err,
variant: "destructive",
});
}}
/>
<div>
<Button type="submit" disabled={loading}>
Expand Down
Loading
Loading