Skip to content

Commit 3079b25

Browse files
committed
New components: Embed and Marquee
1 parent 32c90c7 commit 3079b25

File tree

20 files changed

+1169
-96
lines changed

20 files changed

+1169
-96
lines changed

apps/web/package.json

Lines changed: 94 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,96 @@
11
{
2-
"name": "@courselit/web",
3-
"version": "0.57.12",
4-
"private": true,
5-
"scripts": {
6-
"dev": "next dev",
7-
"build": "next build",
8-
"start": "next start",
9-
"lint": "next lint",
10-
"prettier": "prettier --write **/*.ts"
11-
},
12-
"dependencies": {
13-
"@courselit/common-logic": "workspace:^",
14-
"@courselit/common-models": "workspace:^",
15-
"@courselit/common-widgets": "workspace:^",
16-
"@courselit/components-library": "workspace:^",
17-
"@courselit/icons": "workspace:^",
18-
"@courselit/state-management": "workspace:^",
19-
"@courselit/utils": "workspace:^",
20-
"@hookform/resolvers": "^3.9.1",
21-
"@radix-ui/react-alert-dialog": "^1.1.2",
22-
"@radix-ui/react-avatar": "^1.1.3",
23-
"@radix-ui/react-checkbox": "^1.1.4",
24-
"@radix-ui/react-collapsible": "^1.1.3",
25-
"@radix-ui/react-compose-refs": "^1.1.1",
26-
"@radix-ui/react-dialog": "^1.1.6",
27-
"@radix-ui/react-dropdown-menu": "^2.1.6",
28-
"@radix-ui/react-label": "^2.1.2",
29-
"@radix-ui/react-popover": "^1.1.6",
30-
"@radix-ui/react-radio-group": "^1.2.3",
31-
"@radix-ui/react-scroll-area": "^1.2.3",
32-
"@radix-ui/react-select": "^2.1.6",
33-
"@radix-ui/react-separator": "^1.1.2",
34-
"@radix-ui/react-slot": "^1.1.2",
35-
"@radix-ui/react-switch": "^1.1.3",
36-
"@radix-ui/react-tabs": "^1.1.3",
37-
"@radix-ui/react-toast": "^1.2.6",
38-
"@radix-ui/react-tooltip": "^1.1.8",
39-
"@radix-ui/react-visually-hidden": "^1.1.0",
40-
"@stripe/stripe-js": "^5.4.0",
41-
"@types/base-64": "^1.0.0",
42-
"archiver": "^5.3.1",
43-
"aws4": "^1.13.2",
44-
"base-64": "^1.0.0",
45-
"chart.js": "^4.4.7",
46-
"class-variance-authority": "^0.7.0",
47-
"clsx": "^2.1.1",
48-
"cookie": "^0.4.2",
49-
"date-fns": "^4.1.0",
50-
"graphql": "^16.10.0",
51-
"graphql-type-json": "^0.3.2",
52-
"lodash.debounce": "^4.0.8",
53-
"lucide-react": "^0.475.0",
54-
"mongodb": "^6.15.0",
55-
"mongoose": "^8.13.1",
56-
"next": "^14.2.4",
57-
"next-auth": "5.0.0-beta.19",
58-
"nodemailer": "^6.7.2",
59-
"pug": "^3.0.2",
60-
"razorpay": "^2.9.4",
61-
"react": "^18.2.0",
62-
"react-chartjs-2": "^5.3.0",
63-
"react-csv": "^2.2.2",
64-
"react-dom": "^18.2.0",
65-
"react-hook-form": "^7.54.1",
66-
"react-redux": "^8.1.2",
67-
"recharts": "^2.15.1",
68-
"remirror": "^3.0.1",
69-
"sharp": "^0.33.2",
70-
"slugify": "^1.6.5",
71-
"stripe": "^17.5.0",
72-
"tailwind-merge": "^2.5.4",
73-
"tailwindcss-animate": "^1.0.7",
74-
"zod": "^3.24.1"
75-
},
76-
"devDependencies": {
77-
"@types/bcryptjs": "^2.4.2",
78-
"@types/cookie": "^0.4.1",
79-
"@types/mongodb": "^4.0.7",
80-
"@types/node": "17.0.21",
81-
"@types/nodemailer": "^6.4.4",
82-
"@types/pug": "^2.0.6",
83-
"@types/react": "18.2.31",
84-
"@types/react-redux": "^7.1.23",
85-
"@types/redux-thunk": "^2.1.0",
86-
"eslint": "8.48.0",
87-
"eslint-config-next": "^14.0.4",
88-
"eslint-config-prettier": "^9.0.0",
89-
"postcss": "^8.4.27",
90-
"prettier": "^3.0.2",
91-
"tailwind-config": "workspace:^",
92-
"tailwindcss": "^3.4.1",
93-
"tsconfig": "workspace:^",
94-
"typescript": "^5.6.2"
95-
}
2+
"name": "@courselit/web",
3+
"version": "0.57.12",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint",
10+
"prettier": "prettier --write **/*.ts"
11+
},
12+
"dependencies": {
13+
"@courselit/common-logic": "workspace:^",
14+
"@courselit/common-models": "workspace:^",
15+
"@courselit/common-widgets": "workspace:^",
16+
"@courselit/components-library": "workspace:^",
17+
"@courselit/icons": "workspace:^",
18+
"@courselit/state-management": "workspace:^",
19+
"@courselit/utils": "workspace:^",
20+
"@hookform/resolvers": "^3.9.1",
21+
"@radix-ui/react-alert-dialog": "^1.1.2",
22+
"@radix-ui/react-avatar": "^1.1.3",
23+
"@radix-ui/react-checkbox": "^1.1.4",
24+
"@radix-ui/react-collapsible": "^1.1.3",
25+
"@radix-ui/react-compose-refs": "^1.1.1",
26+
"@radix-ui/react-dialog": "^1.1.6",
27+
"@radix-ui/react-dropdown-menu": "^2.1.6",
28+
"@radix-ui/react-label": "^2.1.2",
29+
"@radix-ui/react-popover": "^1.1.6",
30+
"@radix-ui/react-radio-group": "^1.2.3",
31+
"@radix-ui/react-scroll-area": "^1.2.3",
32+
"@radix-ui/react-select": "^2.1.6",
33+
"@radix-ui/react-separator": "^1.1.2",
34+
"@radix-ui/react-slot": "^1.1.2",
35+
"@radix-ui/react-switch": "^1.1.3",
36+
"@radix-ui/react-tabs": "^1.1.3",
37+
"@radix-ui/react-toast": "^1.2.6",
38+
"@radix-ui/react-tooltip": "^1.1.8",
39+
"@radix-ui/react-visually-hidden": "^1.1.0",
40+
"@stripe/stripe-js": "^5.4.0",
41+
"@types/base-64": "^1.0.0",
42+
"archiver": "^5.3.1",
43+
"aws4": "^1.13.2",
44+
"base-64": "^1.0.0",
45+
"chart.js": "^4.4.7",
46+
"class-variance-authority": "^0.7.0",
47+
"clsx": "^2.1.1",
48+
"cookie": "^0.4.2",
49+
"date-fns": "^4.1.0",
50+
"graphql": "^16.10.0",
51+
"graphql-type-json": "^0.3.2",
52+
"lodash.debounce": "^4.0.8",
53+
"lucide-react": "^0.475.0",
54+
"mongodb": "^6.15.0",
55+
"mongoose": "^8.13.1",
56+
"next": "^14.2.4",
57+
"next-auth": "5.0.0-beta.19",
58+
"nodemailer": "^6.7.2",
59+
"pug": "^3.0.2",
60+
"razorpay": "^2.9.4",
61+
"react": "^18.2.0",
62+
"react-chartjs-2": "^5.3.0",
63+
"react-csv": "^2.2.2",
64+
"react-dom": "^18.2.0",
65+
"react-hook-form": "^7.54.1",
66+
"react-redux": "^8.1.2",
67+
"recharts": "^2.15.1",
68+
"remirror": "^3.0.1",
69+
"sharp": "^0.33.2",
70+
"slugify": "^1.6.5",
71+
"stripe": "^17.5.0",
72+
"tailwind-merge": "^2.5.4",
73+
"tailwindcss-animate": "^1.0.7",
74+
"zod": "^3.24.1"
75+
},
76+
"devDependencies": {
77+
"@types/bcryptjs": "^2.4.2",
78+
"@types/cookie": "^0.4.1",
79+
"@types/mongodb": "^4.0.7",
80+
"@types/node": "17.0.21",
81+
"@types/nodemailer": "^6.4.4",
82+
"@types/pug": "^2.0.6",
83+
"@types/react": "18.2.31",
84+
"@types/react-redux": "^7.1.23",
85+
"@types/redux-thunk": "^2.1.0",
86+
"eslint": "8.48.0",
87+
"eslint-config-next": "^14.0.4",
88+
"eslint-config-prettier": "^9.0.0",
89+
"postcss": "^8.4.27",
90+
"prettier": "^3.0.2",
91+
"tailwind-config": "workspace:^",
92+
"tailwindcss": "^3.4.1",
93+
"tsconfig": "workspace:^",
94+
"typescript": "^5.6.2"
95+
}
9696
}

apps/web/ui-config/widgets.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
FAQ,
1313
Pricing,
1414
Media,
15+
Embed,
16+
Marquee,
1517
} from "@courselit/common-widgets";
1618

1719
function loadWidgets(): Record<string, any> {
@@ -27,6 +29,8 @@ function loadWidgets(): Record<string, any> {
2729
widgets[FAQ.metadata.name] = FAQ;
2830
widgets[Pricing.metadata.name] = Pricing;
2931
widgets[Media.metadata.name] = Media;
32+
widgets[Embed.metadata.name] = Embed;
33+
widgets[Marquee.metadata.name] = Marquee;
3034
widgets[Footer.metadata.name] = Object.assign({}, Footer, { shared: true });
3135
widgets[Header.metadata.name] = Object.assign({}, Header, { shared: true });
3236
widgets[EmailForm.metadata.name] = Object.assign({}, EmailForm, {
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import React, { ChangeEvent, useEffect, useState } from "react";
2+
import Settings from "./settings";
3+
import {
4+
horizontalPadding as defaultHorizontalPadding,
5+
verticalPadding as defaultVerticalPadding,
6+
height as defaultHeight,
7+
} from "./defaults";
8+
import {
9+
CssIdField,
10+
AdminWidgetPanel,
11+
ColorSelector,
12+
ContentPaddingSelector,
13+
Form,
14+
FormField,
15+
Select,
16+
Textarea,
17+
AlertTitle,
18+
AlertDescription,
19+
Alert,
20+
} from "@courselit/components-library";
21+
import { AlertCircle, Lightbulb } from "lucide-react";
22+
23+
export default function AdminWidget({
24+
settings,
25+
onChange,
26+
}: {
27+
settings: Settings;
28+
onChange: (...args: any[]) => void;
29+
}) {
30+
const [backgroundColor, setBackgroundColor] = useState(
31+
settings.backgroundColor,
32+
);
33+
const [url, setUrl] = useState(settings.url);
34+
const [script, setScript] = useState(settings.script);
35+
const [aspectRatio, setAspectRatio] = useState(settings.aspectRatio);
36+
const [height, setHeight] = useState(settings.height || defaultHeight);
37+
const [horizontalPadding, setHorizontalPadding] = useState<number>(
38+
settings.horizontalPadding || defaultHorizontalPadding,
39+
);
40+
const [verticalPadding, setVerticalPadding] = useState<number>(
41+
settings.verticalPadding || defaultVerticalPadding,
42+
);
43+
const [cssId, setCssId] = useState(settings.cssId);
44+
45+
useEffect(() => {
46+
onChange({
47+
backgroundColor,
48+
url,
49+
script,
50+
aspectRatio,
51+
height,
52+
horizontalPadding,
53+
verticalPadding,
54+
cssId,
55+
});
56+
}, [
57+
backgroundColor,
58+
url,
59+
script,
60+
aspectRatio,
61+
height,
62+
horizontalPadding,
63+
verticalPadding,
64+
cssId,
65+
]);
66+
67+
return (
68+
<div className="flex flex-col gap-4">
69+
<Alert variant="destructive">
70+
<AlertTitle>
71+
<span className="flex items-center gap-2">
72+
<AlertCircle className="w-4 h-4" /> Beware
73+
</span>
74+
</AlertTitle>
75+
<AlertDescription className="text-sm">
76+
Embedding external content may have security implications.
77+
Only embed content from trusted sources.
78+
</AlertDescription>
79+
</Alert>
80+
<AdminWidgetPanel title="Content">
81+
<Form className="flex flex-col gap-4">
82+
<FormField
83+
label="URL"
84+
value={url}
85+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
86+
setUrl(e.target.value)
87+
}
88+
/>
89+
<div className="flex items-center gap-4 my-1">
90+
<hr className="flex-grow border-t border-gray-300" />
91+
<span className="text-gray-500 text-sm font-medium">
92+
OR
93+
</span>
94+
<hr className="flex-grow border-t border-gray-300" />
95+
</div>
96+
<div className="flex flex-col gap-2">
97+
<label className="mb-1 font-semibold">Script</label>
98+
<Textarea
99+
value={script}
100+
rows={10}
101+
onChange={(e: ChangeEvent<HTMLTextAreaElement>) =>
102+
setScript(e.target.value)
103+
}
104+
/>
105+
</div>
106+
<div className="text-xs text-gray-500 flex items-center gap-1">
107+
<Lightbulb className="w-4 h-4" />
108+
<p>
109+
If you use the script option, the URL option will be
110+
ignored.
111+
</p>
112+
</div>
113+
</Form>
114+
</AdminWidgetPanel>
115+
<AdminWidgetPanel title="Design" className="flex flex-col gap-4">
116+
<Form className="flex flex-col gap-4">
117+
<FormField
118+
label="Height"
119+
type="number"
120+
value={height}
121+
min={0}
122+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
123+
setHeight(Number(e.target.value))
124+
}
125+
/>
126+
<Select
127+
title="Aspect ratio"
128+
value={aspectRatio}
129+
onChange={(value?: string) =>
130+
setAspectRatio(value as "16:9" | "4:3" | "1:1")
131+
}
132+
options={[
133+
{ label: "Default", value: "default" },
134+
{ label: "16:9", value: "16:9" },
135+
{ label: "4:3", value: "4:3" },
136+
{ label: "1:1", value: "1:1" },
137+
]}
138+
/>
139+
</Form>
140+
<ColorSelector
141+
title="Background color"
142+
value={backgroundColor || "inherit"}
143+
onChange={(value?: string) => setBackgroundColor(value)}
144+
/>
145+
<ContentPaddingSelector
146+
value={horizontalPadding}
147+
min={50}
148+
onChange={setHorizontalPadding}
149+
/>
150+
<ContentPaddingSelector
151+
variant="vertical"
152+
value={verticalPadding}
153+
onChange={setVerticalPadding}
154+
/>
155+
</AdminWidgetPanel>
156+
<AdminWidgetPanel title="Advanced">
157+
<CssIdField value={cssId} onChange={setCssId} />
158+
</AdminWidgetPanel>
159+
</div>
160+
);
161+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const horizontalPadding = 100;
2+
export const verticalPadding = 50;
3+
export const height = 500;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import AdminWidget from "./admin-widget";
2+
import metadata from "./metadata";
3+
import Widget from "./widget";
4+
import { Widget as WidgetType } from "@courselit/common-models";
5+
6+
export const Embed: WidgetType = {
7+
widget: Widget,
8+
metadata,
9+
adminWidget: AdminWidget,
10+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { WidgetMetadata, Constants } from "@courselit/common-models";
2+
const { PageType } = Constants;
3+
4+
const metadata: WidgetMetadata = {
5+
name: "embed",
6+
displayName: "Embed",
7+
compatibleWith: [PageType.PRODUCT, PageType.SITE, PageType.COMMUNITY],
8+
};
9+
10+
export default metadata;

0 commit comments

Comments
 (0)