Skip to content

Commit a806b8c

Browse files
tech: add vkui-nextjs-ts example (#8404)
* tech: add vkui-nextjs-ts example * fix: fix css file name * tech: add ignore examples * fix: setting eslint * fix: fix Readme * fix: refactor pages * fix: fix yarn version and docs * fix: fix formatting * fix: add meta to adaptivity * fix: fix metadata and auto calculation of color scheme * fix: fix auto detect theme * fix: add .yarnrc.yml * fix: remove rows * fix: move import * fix: add vkui classNames * doc: fix docs * Update examples/vkui-nextjs-ts/README.md Co-authored-by: Inomdzhon Mirdzhamolov <[email protected]> --------- Co-authored-by: Inomdzhon Mirdzhamolov <[email protected]>
1 parent 21d869f commit a806b8c

22 files changed

+4173
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"root": true,
3+
"extends": ["next/core-web-vitals", "next/typescript"]
4+
}

examples/vkui-nextjs-ts/.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
.pnpm-debug.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
# next-env.d.ts

examples/vkui-nextjs-ts/.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

examples/vkui-nextjs-ts/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# VKUI — шаблон приложения на Next.js + TypeScript
2+
3+
[![Открыть в StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/VKCOM/VKUI/tree/master/examples/vkui-nextjs-ts)
4+
[![Открыть в CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/VKCOM/VKUI/tree/master/examples/vkui-nextjs-ts)
5+
6+
Готовый шаблон для быстрого старта проектов с VKUI, Next.js и TypeScript с использованием App Router.
7+
8+
## Как использовать
9+
10+
### 1. Скачать шаблон
11+
12+
Через curl:
13+
14+
```bash
15+
curl https://codeload.github.com/VKCOM/VKUI/tar.gz/master | tar -xz --strip=2 VKUI-master/examples/vkui-nextjs-ts
16+
cd vkui-nextjs-ts
17+
```
18+
19+
Или клонировать репозиторий:
20+
21+
```bash
22+
git clone https://github.com/VKCOM/VKUI.git
23+
cd VKUI/examples/vkui-nextjs-ts
24+
```
25+
26+
### 2. Установить зависимости
27+
28+
```bash
29+
yarn install
30+
```
31+
32+
### 3. Запустить проект
33+
34+
В режиме разработки:
35+
36+
```bash
37+
yarn dev
38+
```
39+
40+
Сборка для production:
41+
42+
```bash
43+
yarn build
44+
```
45+
46+
## О шаблоне
47+
48+
Этот пример демонстрирует:
49+
50+
- Интеграцию VKUI с Next.js App Router
51+
- Готовую конфигурацию TypeScript
52+
- Настройку цветовой схемы приложения (темная/светлая)
53+
- Примеры использования основных компонентов VKUI
54+
55+
## Особенности Next.js
56+
57+
- Использование `App Router` для маршрутизации
58+
- Серверные компоненты для оптимизации производительности
59+
- Автоматическая обработка статических ресурсов
60+
- Встроенная поддержка `TypeScript`
61+
62+
## Что дальше?
63+
64+
1. Изучите [документацию VKUI](https://vkcom.github.io/VKUI) для работы с компонентами
65+
2. Ознакомьтесь с [официальной документацией Next.js](https://nextjs.org/docs)
66+
3. Посмотрите [другие примеры](https://github.com/VKCOM/VKUI/tree/master/examples) интеграций

examples/vkui-nextjs-ts/next-env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
transpilePackages: ['@vkontakte/vkui'],
4+
5+
modularizeImports: {
6+
'@vkontakte/vkui': {
7+
transform: '@vkontakte/vkui/dist/cssm',
8+
skipDefaultConversion: true,
9+
},
10+
},
11+
};
12+
13+
export default nextConfig;

examples/vkui-nextjs-ts/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "vkui-nextjs-ts",
3+
"version": "1.0.0",
4+
"scripts": {
5+
"dev": "next dev",
6+
"build": "next build",
7+
"start": "next start",
8+
"lint": "next lint"
9+
},
10+
"dependencies": {
11+
"@vkontakte/icons": "latest",
12+
"@vkontakte/vkui": "latest",
13+
"next": "^15.0.0",
14+
"react": "^19.0.0",
15+
"react-dom": "^19.0.0"
16+
},
17+
"devDependencies": {
18+
"@types/node": "latest",
19+
"@types/react": "^19.0.0",
20+
"@types/react-dom": "^19.0.0",
21+
"eslint": "^8.56.0",
22+
"eslint-config-next": "^15.0.0",
23+
"typescript": "latest"
24+
},
25+
"packageManager": "[email protected]+sha512.5a0afa1d4c1d844b3447ee3319633797bcd6385d9a44be07993ae52ff4facabccafb4af5dcd1c2f9a94ac113e5e9ff56f6130431905884414229e284e37bb7c9"
26+
}

examples/vkui-nextjs-ts/public/.gitkeep

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { AboutPageContent } from '@/client/pages/AboutPageContent';
2+
3+
export default function About() {
4+
return <AboutPageContent />;
5+
}
25.3 KB
Binary file not shown.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Layout } from '@/client/Layout';
2+
import '@vkontakte/vkui/dist/cssm/styles/themes.css';
3+
import { Metadata, Viewport } from 'next';
4+
import * as React from 'react';
5+
6+
export const viewport: Viewport = {
7+
width: 'device-width',
8+
initialScale: 1,
9+
userScalable: false,
10+
viewportFit: 'cover',
11+
};
12+
13+
export const metadata: Metadata = {
14+
title: 'NextJS + VKUI + TS',
15+
};
16+
17+
export default function RootLayout({ children }: { children: React.ReactNode }) {
18+
return (
19+
<html lang="ru" className="vkui" suppressHydrationWarning>
20+
<body className="vkui__root">
21+
<Layout>{children}</Layout>
22+
</body>
23+
</html>
24+
);
25+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { HomePageContent } from '@/client/pages/HomePageContent';
2+
3+
export default function Page() {
4+
return <HomePageContent />;
5+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import * as React from 'react';
2+
import { ColorScheme, type ColorSchemeType, IconButton, useColorScheme } from '@vkontakte/vkui';
3+
import { Icon28SunOutline, Icon28MoonOutline } from '@vkontakte/icons';
4+
5+
const STORAGE_KEY = 'vkui-next-js-template:color-scheme';
6+
7+
const getColorSchemeFromStorage = (defaultColorScheme: ColorSchemeType) => {
8+
if (typeof window === 'undefined') {
9+
return defaultColorScheme;
10+
}
11+
let colorScheme;
12+
try {
13+
colorScheme = (localStorage.getItem(STORAGE_KEY) as ColorSchemeType) || defaultColorScheme;
14+
} catch {
15+
// Unsupported
16+
}
17+
return colorScheme || defaultColorScheme;
18+
};
19+
20+
const setColorSchemeToStorage = (colorScheme: ColorSchemeType) => {
21+
if (typeof window === 'undefined') {
22+
return undefined;
23+
}
24+
try {
25+
localStorage.setItem(STORAGE_KEY, colorScheme);
26+
} catch {
27+
// Unsupported
28+
}
29+
};
30+
31+
const ColorSchemeSwitcher: React.FC<{
32+
colorScheme: ColorSchemeType;
33+
setColorScheme: (colorScheme: ColorSchemeType) => void;
34+
}> = ({ colorScheme, setColorScheme }) => {
35+
return (
36+
<IconButton
37+
label={`Цветовая схема: ${colorScheme}`}
38+
onClick={() =>
39+
setColorScheme(colorScheme === ColorScheme.LIGHT ? ColorScheme.DARK : ColorScheme.LIGHT)
40+
}
41+
>
42+
{ColorScheme.LIGHT ? <Icon28SunOutline /> : <Icon28MoonOutline />}
43+
</IconButton>
44+
);
45+
};
46+
47+
export const useColorSchemeSwitcher = (): [ColorSchemeType, React.ReactNode] => {
48+
const defaultColorScheme = useColorScheme();
49+
const [colorScheme, setColorScheme] = React.useState<ColorSchemeType>(ColorScheme.LIGHT);
50+
51+
const _updateColorScheme = React.useCallback((colorScheme: ColorSchemeType) => {
52+
setColorScheme(colorScheme);
53+
setColorSchemeToStorage(colorScheme);
54+
}, []);
55+
56+
React.useLayoutEffect(
57+
() => setColorScheme(getColorSchemeFromStorage(defaultColorScheme)),
58+
[_updateColorScheme, defaultColorScheme],
59+
);
60+
61+
return [
62+
colorScheme,
63+
<ColorSchemeSwitcher
64+
key="color-scheme-switcher"
65+
colorScheme={colorScheme}
66+
setColorScheme={_updateColorScheme}
67+
/>,
68+
];
69+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.layout {
2+
block-size: 100%;
3+
}
4+
5+
.header {
6+
padding-block: 5px;
7+
padding-inline: 5px;
8+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use client';
2+
3+
import { useColorSchemeSwitcher } from '@/client/ColorSchemeSwitcher';
4+
import {
5+
AdaptivityProvider,
6+
AppRoot,
7+
ColorSchemeProvider,
8+
ConfigProvider,
9+
FixedLayout,
10+
Flex,
11+
} from '@vkontakte/vkui';
12+
import styles from './Layout.module.css';
13+
14+
interface LayoutProps {
15+
children: React.ReactNode;
16+
}
17+
18+
function LayoutContent({ children }: LayoutProps) {
19+
const [colorScheme, colorSchemeSwitcher] = useColorSchemeSwitcher();
20+
21+
return (
22+
<ColorSchemeProvider value={colorScheme}>
23+
<AppRoot>
24+
<Flex direction="column" justify="center" className={styles.layout}>
25+
<FixedLayout vertical="top">
26+
<Flex justify="end" className={styles.header}>
27+
{colorSchemeSwitcher}
28+
</Flex>
29+
</FixedLayout>
30+
{children}
31+
</Flex>
32+
</AppRoot>
33+
</ColorSchemeProvider>
34+
);
35+
}
36+
37+
export function Layout({ children }: LayoutProps) {
38+
return (
39+
<ConfigProvider>
40+
<AdaptivityProvider>
41+
<LayoutContent>{children}</LayoutContent>
42+
</AdaptivityProvider>
43+
</ConfigProvider>
44+
);
45+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Link, Text } from '@vkontakte/vkui';
2+
3+
export default function Copyright() {
4+
return (
5+
<Text>
6+
{'Авторские права © '}
7+
<Link color="inherit" href="https://vkcom.github.io/VKUI/">
8+
Ваш сайт
9+
</Link>{' '}
10+
{new Date().getFullYear()}.
11+
</Text>
12+
);
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.proTip {
2+
margin-block-start: 6px;
3+
margin-block-end: 3px;
4+
}
5+
6+
.proTipIcon {
7+
display: inline;
8+
margin-inline-end: 2px;
9+
vertical-align: bottom;
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Icon24LightbulbOutline } from '@vkontakte/icons';
2+
import { Headline, Link } from '@vkontakte/vkui';
3+
import styles from './ProTip.module.css';
4+
5+
export default function ProTip() {
6+
return (
7+
<Headline className={styles.proTip}>
8+
<Icon24LightbulbOutline className={styles.proTipIcon} />
9+
{'Совет: посмотрите другие '}
10+
<Link href="https://vkcom.github.io/VKUI/">шаблоны</Link>
11+
{' в документации VKUI.'}
12+
</Headline>
13+
);
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client';
2+
3+
import { Button, Flex, Spacing, Title } from '@vkontakte/vkui';
4+
import NextLink from 'next/link';
5+
import Copyright from '@/client/common/Copyright';
6+
import ProTip from '@/client/common/ProTip';
7+
8+
export function AboutPageContent() {
9+
return (
10+
<Flex direction="column" justify="center" align="center">
11+
<Title level="3" Component="h1">
12+
VKUI - Next.js пример на TypeScript
13+
</Title>
14+
<Spacing />
15+
<Button Component={NextLink} href="/">
16+
Перейти на домашнюю страницу
17+
</Button>
18+
<ProTip />
19+
<Copyright />
20+
</Flex>
21+
);
22+
}

0 commit comments

Comments
 (0)