├── src
├── hooks
│ ├── index.ts
│ └── useSnackbar.ts
├── components
│ ├── navigation
│ │ ├── index.ts
│ │ ├── NavigationTabbar.tsx
│ │ ├── NavigationMenu.tsx
│ │ └── Navigation.tsx
│ └── snackbar
│ │ └── CustomSnackbar.tsx
├── types
│ ├── PopoutProps.ts
│ ├── index.ts
│ ├── NavigationItem.ts
│ └── Snackbar.ts
├── utils
│ ├── index.ts
│ ├── getHashParam.ts
│ └── getPlatform.ts
├── pages
│ ├── index.ts
│ ├── Info.tsx
│ ├── Persik.tsx
│ └── Home.tsx
├── assets
│ └── persik.png
├── eruda.ts
├── decs.d.ts
├── modals
│ ├── index.tsx
│ └── Modal.tsx
├── store
│ └── index.ts
├── index.tsx
├── popouts
│ ├── index.tsx
│ ├── TestAlert.tsx
│ └── TestActionSheet.tsx
├── bridge.ts
└── App.tsx
├── vk-hosting-config.json
├── vite.config.ts
├── .gitignore
├── index.html
├── tsconfig.json
├── LICENSE
├── README.md
├── package.json
└── .eslintrc.json
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useSnackbar'
2 |
--------------------------------------------------------------------------------
/src/components/navigation/index.ts:
--------------------------------------------------------------------------------
1 |
2 | export * from './Navigation'
--------------------------------------------------------------------------------
/src/types/PopoutProps.ts:
--------------------------------------------------------------------------------
1 | export type PopoutProps = {
2 | nav: string
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './getHashParam'
2 | export * from './getPlatform'
3 |
--------------------------------------------------------------------------------
/src/pages/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Home'
2 | export * from './Persik'
3 | export * from './Info'
--------------------------------------------------------------------------------
/src/assets/persik.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vladyoslav/vk-mini-apps-template/HEAD/src/assets/persik.png
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Snackbar'
2 | export * from './NavigationItem'
3 | export * from './PopoutProps'
4 |
--------------------------------------------------------------------------------
/src/utils/getHashParam.ts:
--------------------------------------------------------------------------------
1 |
2 | export const getHashParam = (param: string): string | null => {
3 | return new URLSearchParams(window.location.search).get(param)
4 | }
--------------------------------------------------------------------------------
/src/types/NavigationItem.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement } from 'react'
2 |
3 | export type NavigationItem = {
4 | to: string,
5 | text: string,
6 | icon: ReactElement
7 | }
--------------------------------------------------------------------------------
/src/types/Snackbar.ts:
--------------------------------------------------------------------------------
1 |
2 | export type Snackbar = {
3 | type: SnackbarType
4 | text: string
5 | }
6 |
7 | export enum SnackbarType {
8 | DONE,
9 | ERROR
10 | }
--------------------------------------------------------------------------------
/vk-hosting-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "static_path": "dist",
3 | "app_id": 7974220,
4 | "endpoints": {
5 | "mobile": "index.html",
6 | "mvk": "index.html",
7 | "web": "index.html"
8 | }
9 | }
--------------------------------------------------------------------------------
/src/eruda.ts:
--------------------------------------------------------------------------------
1 | import eruda from 'eruda'
2 | import erudaCode from 'eruda-code'
3 | import erudaDom from 'eruda-dom'
4 | eruda.init()
5 | eruda.add(erudaCode)
6 | eruda.add(erudaDom)
7 |
8 | export default eruda
--------------------------------------------------------------------------------
/src/decs.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'eruda' {
2 | export function init()
3 | export function add(module: any)
4 | }
5 |
6 | declare module 'eruda-code'
7 | declare module 'eruda-dom'
8 |
9 | declare module '*.png'
10 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import reactRefresh from '@vitejs/plugin-react-refresh'
3 |
4 | export default defineConfig({
5 | plugins: [reactRefresh()],
6 | server: {
7 | port: 10888,
8 | https: true
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/src/modals/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Modal } from './Modal'
3 | import { ModalRoot } from '@cteamdev/router'
4 |
5 | export const Modals = () => {
6 | return (
7 |
8 |
9 |
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { atom } from '@mntm/precoil'
2 | import { UserInfo } from '@vkontakte/vk-bridge'
3 | import { Snackbar } from '../types'
4 |
5 | export const vkUserAtom = atom({} as UserInfo, 'vkUser')
6 |
7 | export const snackbarAtom = atom(undefined, 'snackbar')
8 |
--------------------------------------------------------------------------------
/src/hooks/useSnackbar.ts:
--------------------------------------------------------------------------------
1 | import { snackbarAtom } from '../store'
2 | import { SnackbarType } from '../types'
3 |
4 | const setSnackbar = snackbarAtom.set
5 |
6 | export const setDoneSnackbar = (text: string) => setSnackbar({ type: SnackbarType.DONE, text })
7 |
8 | export const setErrorSnackbar = (text: string) => setSnackbar({ type: SnackbarType.ERROR, text })
9 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import { App } from './App'
4 | import { init, Router } from '@cteamdev/router'
5 | import './bridge'
6 |
7 | init()
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('root')
14 | )
15 |
16 | if (process.env.NODE_ENV === 'development') import('./eruda')
17 |
--------------------------------------------------------------------------------
/src/popouts/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { PopoutRoot } from '@cteamdev/router'
3 | import { TestAlert } from './TestAlert'
4 | import { TestActionSheet } from './TestActionSheet'
5 |
6 | export const Popouts = () => {
7 | return (
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/bridge.ts:
--------------------------------------------------------------------------------
1 | import bridge, { AnyReceiveMethodName, VKBridgeEvent } from '@vkontakte/vk-bridge'
2 |
3 | bridge.subscribe((e: VKBridgeEvent) => {
4 | if (e.detail.type === 'VKWebAppUpdateConfig') {
5 | const scheme: string = e.detail.data.scheme || 'client_light'
6 | document.body.setAttribute('scheme', scheme)
7 | }
8 | })
9 |
10 | bridge.send('VKWebAppInit').then()
11 |
--------------------------------------------------------------------------------
/src/utils/getPlatform.ts:
--------------------------------------------------------------------------------
1 | import { ANDROID, IOS, VKCOM, PlatformType } from '@vkontakte/vkui'
2 | import { getHashParam } from './getHashParam'
3 |
4 | const platforms: Record = {
5 | iphone: IOS,
6 | mobile_web: IOS,
7 | android: ANDROID,
8 | desktop_web: VKCOM
9 | }
10 |
11 | export const getPlatform = (): PlatformType => {
12 | const rawPlatform: string | null = getHashParam('vk_platform')
13 |
14 | return platforms[rawPlatform || 'desktop_web']
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
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 | # production
12 | /build
13 | /dist
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # editors
27 | .idea
28 | .vscode
29 |
30 | # linter
31 | .eslintcache
32 |
33 | src/react-app-env.d.ts
34 | build.zip
35 |
--------------------------------------------------------------------------------
/src/pages/Info.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Group, Panel, PanelHeader, PanelProps, Placeholder } from '@vkontakte/vkui'
3 | import { Icon56GhostOutline } from '@vkontakte/icons'
4 |
5 | export const Info: React.FC = ({ nav }: PanelProps) => {
6 | return (
7 |
8 | Инфо
9 |
10 | }
12 | >
13 | Здесь ничего нет!
14 |
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | VK Mini Apps template
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": false,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/src/popouts/TestAlert.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Alert } from '@vkontakte/vkui'
3 | import { PopoutProps } from '../types'
4 | import { back } from '@cteamdev/router'
5 |
6 | export const TestAlert: React.FC = () => {
7 |
8 | return (
9 |
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/src/modals/Modal.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button, ModalCard, ModalCardProps } from '@vkontakte/vkui'
3 | import { Icon56GhostOutline } from '@vkontakte/icons'
4 | import { back } from '@cteamdev/router'
5 |
6 | export const Modal: React.FC = ({ nav }: ModalCardProps) => {
7 | return (
8 | }
12 | header='Это модальная карточка'
13 | subheader='Это текст модальной карточки'
14 | actions={
15 |
18 | }
19 | />
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/pages/Persik.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Group,
4 | Panel,
5 | PanelHeader,
6 | PanelHeaderBack,
7 | Placeholder,
8 | PanelProps
9 | } from '@vkontakte/vkui'
10 | import persikImage from '../assets/persik.png'
11 | import { back } from '@cteamdev/router'
12 |
13 | export const Persik: React.FC = ({ nav }: PanelProps) => {
14 | return (
15 |
16 | }
18 | >
19 | Персик
20 |
21 |
22 | }
24 | />
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/popouts/TestActionSheet.tsx:
--------------------------------------------------------------------------------
1 | import React, { Ref, useRef } from 'react'
2 | import { ActionSheet, ActionSheetItem } from '@vkontakte/vkui'
3 | import { PopoutProps } from '../types'
4 | import { back } from '@cteamdev/router'
5 |
6 | export const TestActionSheet: React.FC = () => {
7 | const ref: Ref = useRef(document.getElementById('ShowAlert'))
8 |
9 | return (
10 | Отменить}
13 | toggleRef={ref}
14 | >
15 |
16 | Это первая опция
17 |
18 |
19 | Это вторая опция
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/navigation/NavigationTabbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Tabbar, TabbarItem } from '@vkontakte/vkui'
3 | import { NavigationItem } from '../../types'
4 | import { replace, useCurrentState } from '@cteamdev/router'
5 |
6 | type NavigationTabbarProps = {
7 | items: NavigationItem[]
8 | }
9 |
10 | export const NavigationTabbar: React.FC = ({ items }: NavigationTabbarProps) => {
11 | const { view } = useCurrentState()
12 |
13 | return (
14 |
15 | {items.map(item =>
16 | item.to !== view && replace(item.to)}
21 | >
22 | {item.icon}
23 |
24 | )}
25 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 VladYoSlav
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/navigation/NavigationMenu.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Group,
4 | Panel,
5 | SimpleCell,
6 | SplitCol
7 | } from '@vkontakte/vkui'
8 | import { NavigationItem } from '../../types'
9 | import { replace, useCurrentState } from '@cteamdev/router'
10 |
11 | type NavigationMenuProps = {
12 | items: NavigationItem[]
13 | }
14 |
15 | export const NavigationMenu: React.FC = ({ items }: NavigationMenuProps) => {
16 | const { view } = useCurrentState()
17 |
18 | return (
19 |
20 |
21 |
22 | {items.map(item =>
23 | item.to !== view && replace(item.to)}
28 | style={item.to === view ? { backgroundColor: 'var(--button_secondary_background)' } : {}}
29 | >
30 | {item.text}
31 |
32 | )}
33 |
34 |
35 |
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/snackbar/CustomSnackbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { useAtomState } from '@mntm/precoil'
3 | import { snackbarAtom } from '../../store'
4 | import { SnackbarType } from '../../types'
5 | import { Icon20CancelCircleFillRed, Icon20CheckCircleFillGreen } from '@vkontakte/icons'
6 | import { Snackbar } from '@vkontakte/vkui'
7 |
8 | type CustomSnackbarProps = {
9 | isDesktop: boolean
10 | }
11 |
12 | export const CustomSnackbar: React.FC = ({ isDesktop }: CustomSnackbarProps) => {
13 | const [snackbar, setSnackbar] = useAtomState(snackbarAtom)
14 |
15 | return snackbar
16 | ? (
17 | setSnackbar(undefined)}
19 | before={snackbar?.type === SnackbarType.DONE
20 | ?
21 | :
22 | }
23 | style={!isDesktop
24 | ? { marginBottom: 'calc(var(--tabbar_height) + var(--safe-area-inset-bottom))' }
25 | : {}
26 | }
27 | >
28 | {snackbar?.text}
29 |
30 | )
31 | : <>>
32 | }
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VK Mini Apps template
2 | TypeScript шаблон для фронтенда мини-приложений
3 |
4 | ## Использует
5 | * TypeScript
6 | * React
7 | * Библиотеку компонентов VKUI
8 | * Роутер [@cteamdev/router](https://github.com/cteamdev/router)
9 | * [@mntm/precoil](https://github.com/maxi-team/precoil) в качестве стейт-менеджера
10 | * Инструмент для сборки Vite
11 | * Линтер ESLint
12 |
13 | Есть ветка с роутером [@unexp/router](https://github.com/land47/router)
14 |
15 | ## Установка
16 | [Создайте репозиторий, используя этот шаблон](https://github.com/vladyoslav/vk-mini-apps-template/generate)
17 |
18 | Установите зависимости с помощью `yarn` (рекомендованный способ):
19 | ```shell
20 | yarn
21 | ```
22 |
23 | Или `npm`:
24 | ```shell
25 | npm i
26 | ```
27 |
28 | ## Запуск
29 | ```shell
30 | yarn start
31 | ```
32 | или
33 | ```shell
34 | npm start
35 | ```
36 |
37 | ## Деплой
38 | Замените `app_id` в `vk-hosting-config.json` на айди своего приложения ВКонтакте
39 |
40 | Задеплойте приложение на хостинг статики ВКонтакте с помощью:
41 | ```shell
42 | yarn run deploy
43 | ```
44 | или
45 | ```shell
46 | npm run deploy
47 | ```
48 | После этого приложение будет доступно по ссылке вида https://prod-app...
49 |
50 | Готово!
51 |
52 | ## Пример
53 | >https://vk.com/app7974220
54 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import {
3 | AdaptivityProvider,
4 | AppRoot,
5 | ConfigProvider,
6 | PlatformType
7 | } from '@vkontakte/vkui'
8 | import '@vkontakte/vkui/dist/vkui.css'
9 | import { View } from '@cteamdev/router'
10 | import { Home, Info, Persik } from './pages'
11 | import { Navigation } from './components/navigation'
12 | import { getPlatform } from './utils'
13 | import { useSetAtomState } from '@mntm/precoil'
14 | import { vkUserAtom } from './store'
15 | import bridge, { UserInfo } from '@vkontakte/vk-bridge'
16 |
17 | export const App: React.FC = () => {
18 | const platform: PlatformType = getPlatform()
19 | const setVkUser = useSetAtomState(vkUserAtom)
20 |
21 | useEffect(() => {
22 | const load = async () => {
23 | const vkUser: UserInfo = await bridge.send('VKWebAppGetUserInfo')
24 | setVkUser(vkUser)
25 | }
26 |
27 | load()
28 | }, [])
29 |
30 | return (
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vk-mini-apps-template",
3 | "version": "1.0.0",
4 | "main": "index.tsx",
5 | "homepage": "./",
6 | "scripts": {
7 | "start": "vite",
8 | "build": "cross-env GENERATE_SOURCEMAP=false vite build",
9 | "predeploy": "yarn run build",
10 | "deploy": "vk-miniapps-deploy"
11 | },
12 | "devDependencies": {
13 | "@types/node": "^12.0.0",
14 | "@types/react": "^17.0.0",
15 | "@types/react-dom": "^17.0.0",
16 | "@typescript-eslint/eslint-plugin": "^4.33.0",
17 | "@typescript-eslint/parser": "^4.33.0",
18 | "@vitejs/plugin-react-refresh": "^1.3.6",
19 | "@vkontakte/vk-miniapps-deploy": "^0.0.25",
20 | "cross-env": "^7.0.3",
21 | "eslint": "^7.32.0",
22 | "eslint-plugin-import": "^2.24.2",
23 | "eslint-plugin-promise": "^5.1.0",
24 | "eslint-plugin-react": "^7.26.1",
25 | "eslint-plugin-standard": "^5.0.0",
26 | "typescript": "^4.4.4",
27 | "vite": "^2.6.10"
28 | },
29 | "dependencies": {
30 | "@cteamdev/router": "^0.2.6",
31 | "@mntm/precoil": "^4.0.4",
32 | "@vkontakte/icons": "^1.129.0",
33 | "@vkontakte/vk-bridge": "^2.4.8",
34 | "@vkontakte/vkui": "^4.17.0",
35 | "eruda": "^2.4.1",
36 | "eruda-code": "^2.0.0",
37 | "eruda-dom": "^2.0.0",
38 | "react": "^17.0.2",
39 | "react-dom": "^17.0.2"
40 | },
41 | "browserslist": {
42 | "production": [
43 | ">0.2%",
44 | "not dead",
45 | "not op_mini all"
46 | ],
47 | "development": [
48 | "last 1 chrome version",
49 | "last 1 firefox version",
50 | "last 1 safari version"
51 | ]
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": [
7 | "plugin:react/recommended",
8 | "plugin:@typescript-eslint/recommended"
9 | ],
10 | "parser": "@typescript-eslint/parser",
11 | "parserOptions": {
12 | "ecmaFeatures": {
13 | "jsx": true
14 | },
15 | "ecmaVersion": 12,
16 | "sourceType": "module"
17 | },
18 | "plugins": [
19 | "react",
20 | "@typescript-eslint"
21 | ],
22 | "rules": {
23 | "semi": ["error", "never"],
24 | "quotes": ["error", "single"],
25 | "jsx-quotes": ["error", "prefer-single"],
26 | "prefer-const": "error",
27 | "no-var": "error",
28 | "indent": ["error", 2],
29 | "prefer-arrow-callback": "error",
30 | "no-duplicate-imports": "error",
31 | "object-curly-spacing": ["error", "always"],
32 | "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 0 }],
33 | "comma-dangle": ["error", "never"],
34 |
35 | "react/jsx-closing-bracket-location": [2, "tag-aligned"],
36 | "react/jsx-curly-spacing": ["error", { "when": "never", "children": true }],
37 | "react/jsx-indent": ["error", 2],
38 | "react/jsx-indent-props": ["error", 2],
39 | "react/jsx-space-before-closing": [2, "always"],
40 |
41 | "@typescript-eslint/no-explicit-any": "off",
42 | "@typescript-eslint/ban-ts-comment": "off",
43 | "@typescript-eslint/explicit-module-boundary-types": "off",
44 | "@typescript-eslint/no-inferrable-types": "off",
45 | "@typescript-eslint/no-non-null-assertion": "off"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/navigation/Navigation.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactNode } from 'react'
2 | import {
3 | PanelHeader,
4 | SplitCol,
5 | SplitLayout,
6 | useAdaptivity,
7 | ViewWidth
8 | } from '@vkontakte/vkui'
9 | import { NavigationMenu } from './NavigationMenu'
10 | import { Icon28InfoOutline, Icon28UserCircleOutline } from '@vkontakte/icons'
11 | import { NavigationTabbar } from './NavigationTabbar'
12 | import { Modals } from '../../modals'
13 | import { NavigationItem } from '../../types'
14 | import { CustomSnackbar } from '../snackbar/CustomSnackbar'
15 | import { Structure, Epic } from '@cteamdev/router'
16 | import { Popouts } from '../../popouts'
17 |
18 | const items: NavigationItem[] = [
19 | { to: '/', text: 'Главная', icon: },
20 | { to: '/info', text: 'Инфо', icon: }
21 | ]
22 |
23 | type NavigationProps = {
24 | children: ReactNode
25 | }
26 |
27 | export const Navigation: React.FC = ({ children }: NavigationProps) => {
28 | const { viewWidth } = useAdaptivity()
29 | const isDesktop: boolean = (viewWidth ?? 0) >= ViewWidth.SMALL_TABLET
30 |
31 | return (
32 |
33 | }
35 | style={{ justifyContent: 'center' }}
36 | modal={}
37 | popout={}
38 | >
39 |
44 | }>
45 | {children}
46 |
47 |
48 |
49 | {isDesktop && }
50 |
51 |
52 | )
53 | }
54 |
--------------------------------------------------------------------------------
/src/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Avatar, Group, Panel, PanelHeader, PanelProps, SimpleCell } from '@vkontakte/vkui'
3 | import {
4 | Icon28BillheadOutline,
5 | Icon28ChevronRightOutline,
6 | Icon28CheckCircleOutline,
7 | Icon28CancelCircleOutline,
8 | Icon28PawOutline,
9 | Icon28WarningTriangleOutline,
10 | Icon28ArticleOutline
11 | } from '@vkontakte/icons'
12 | import { UserInfo } from '@vkontakte/vk-bridge'
13 | import { useAtomValue } from '@mntm/precoil'
14 | import { vkUserAtom } from '../store'
15 | import { setDoneSnackbar, setErrorSnackbar } from '../hooks'
16 | import { push } from '@cteamdev/router'
17 |
18 | export const Home: React.FC = ({ nav }: PanelProps) => {
19 | const vkUser: UserInfo = useAtomValue(vkUserAtom)
20 |
21 | return (
22 |
23 | Главная
24 |
25 |
28 | }
29 | description='Это же ты!'
30 | >
31 | {vkUser.first_name} {vkUser.last_name}
32 |
33 |
34 |
35 | }
37 | after={}
38 | onClick={() => push('/persik')}
39 | >
40 | Покажи Персика!
41 |
42 |
43 |
44 | }
46 | onClick={() => push('/?modal=modal')}
47 | >
48 | Покажи модальную карточку
49 |
50 |
51 |
52 | }
54 | onClick={() => push('/?popout=alert')}
55 | >
56 | Покажи алерт
57 |
58 | }
61 | onClick={() => push('/?popout=action-sheet')}
62 | >
63 | Покажи список опций
64 |
65 |
66 |
67 | }
69 | onClick={() => setDoneSnackbar('Это добрый снекбар')}
70 | >
71 | Покажи добрый снекбар
72 |
73 | }
75 | onClick={() => setErrorSnackbar('Это злой снекбар')}
76 | >
77 | Покажи злой снекбар
78 |
79 |
80 |
81 | )
82 | }
83 |
--------------------------------------------------------------------------------