├── .env
├── .github
├── README.md
├── webapp-start.png
└── workflows
│ └── gp-deploy.yaml
├── .gitignore
├── .prettierrc
├── .vscode
├── settings-create-vwa.json
└── settings.json
├── README.md
├── eslint.config.js
├── index.html
├── package.json
├── pnpm-lock.yaml
├── public
├── assets
│ └── images
│ │ ├── apple-touch-icon.png
│ │ ├── favicon-32x32.png
│ │ ├── logo-128.png
│ │ ├── logo-48.png
│ │ ├── logo-512.png
│ │ ├── logo-small.png
│ │ ├── logo.png
│ │ └── logo.svg
├── favicon.ico
├── manifest.json
└── service-worker.js
├── src
├── App.vue
├── assets
│ ├── images
│ │ ├── about.svg
│ │ ├── change-account.svg
│ │ ├── contact-us.svg
│ │ ├── hamburger.svg
│ │ ├── home.svg
│ │ ├── icon-plus-blue.svg
│ │ ├── icon-plus-purple.svg
│ │ ├── instagram.svg
│ │ ├── logo.png
│ │ ├── logo.webp
│ │ ├── logo1.svg
│ │ ├── logout.svg
│ │ ├── moon.svg
│ │ ├── sun.svg
│ │ ├── twitter.svg
│ │ └── youtube.svg
│ └── styles
│ │ ├── base.css
│ │ ├── custom.css
│ │ └── vars.css
├── components
│ ├── AppContentPane.vue
│ ├── SectionHeader.vue
│ ├── drawers
│ │ ├── SimpleDrawer.vue
│ │ └── TouchSlideoutDrawer.vue
│ ├── footers
│ │ ├── DistributedFooter.vue
│ │ ├── MantineRichFooter.vue
│ │ ├── MantineSimpleFooter.vue
│ │ ├── RichFooter.vue
│ │ └── SimpleFooter.vue
│ ├── headers
│ │ ├── MantineLayeredHeader.vue
│ │ ├── MantineSimpleHeader.vue
│ │ ├── SimpleHeader.vue
│ │ └── SlidingHeader.vue
│ ├── navbars
│ │ ├── MantineSimpleNavbar.vue
│ │ └── SimpleNavbar.vue
│ └── ui
│ │ ├── BaseIcon.vue
│ │ ├── BaseToggle.vue
│ │ ├── HamburgerIcon.vue
│ │ └── ThemeToggle.vue
├── composables
│ ├── useAppConfig.ts
│ ├── useI18nLight.ts
│ ├── useScreenWidth.ts
│ ├── useSplashScreen.ts
│ └── useTouchSwipe.ts
├── layouts
│ └── MainLayout.vue
├── main.ts
├── router
│ ├── index.ts
│ └── routes.ts
├── services
│ └── api
│ │ ├── http.ts
│ │ ├── index.ts
│ │ ├── interceptors.ts
│ │ ├── jsonrpc.interfaces.ts
│ │ ├── jsonrpc.ts
│ │ ├── notes.txt
│ │ ├── utils.ts
│ │ └── xhr.js
├── utils
│ ├── icons.ts
│ ├── injections
│ │ ├── gtag.html
│ │ ├── injection-config.ts
│ │ ├── open-graph.html
│ │ ├── splash-screen.html
│ │ └── sw.js
│ └── locales
│ │ ├── en.json
│ │ └── es.json
└── views
│ ├── AboutView.vue
│ ├── ContactsView.vue
│ └── HomeView.vue
├── tsconfig.json
└── vite.config.ts
/.env:
--------------------------------------------------------------------------------
1 | VITE_APP_NAME="Vue 3 base website template"
2 | VITE_APP_DEFAULT_LOCALE="en"
3 | VITE_API_URL="/rpc/"
4 |
--------------------------------------------------------------------------------
/.github/README.md:
--------------------------------------------------------------------------------
1 | # Vue 3 webapp builder
2 |
3 | ###
4 |
5 | Full documentation is available at [https://vue-faq.org/en/vue-webapp/](https://vue-faq.org/en/vue-webapp/)
6 |
7 | ## Short description
8 |
9 | Vue 3 webapp builder allows to scaffold a web application, with the ability to choose a business template (portfolio, blog, store, etc.), website layout, design and functional elements (API module, i18n, PWA, splash screen, auth module, themes, etc.), for further customization and content filling.
10 |
11 | ```sh
12 | $ pnpm create vue-webapp
13 |
14 |
15 | ✔ Project name: ... my-vue-project
16 | ✔ Make it PWA ( adds service worker and manifest )? ... yes
17 | ✔ Add Github Action Workflow for publishing it on GitHub Pages? ... no
18 | ✔ Select navigation drawer ' TouchSlideoutDrawer
19 | ✔ Select webapp footer ' RichFooter
20 | ✔ Add 'BaseIcon' component? ... yes
21 | ...
22 |
23 | Scaffolding project in /home/ubuntu/my-vue-project...
24 |
25 | $ cd my-vue-project
26 | $ pnpm i
27 | $ pnpm dev
28 | ```
29 |
30 | Result in a browser ([https://vuesence.github.io/vue-webapp](https://vuesence.github.io/vue-webapp/)):
31 |
32 | 
33 |
34 |
35 | ## Reasons
36 |
37 | There are quite a few (mostly specific and quickly deprecated) boilerplates for creating a Vue application. Usually by these they mean creating an empty project with specific libraries. In other words, it's just setting up an environment to start development without, directly, code.
38 |
39 | At the same time, many other frontend and backend frameworks have starter-kits that allow you to quickly create a ready-made blog, online store, business card site, portfolio, documentation, etc., which greatly helps in learning the framework itself, relevant technologies and best practices, as well as for solving business tasks.
40 |
41 | As a result, one may get the impression that Vue is a rather low-level framework, and to create web applications quickly, efficiently and conveniently, you need to take some add-on or other solution - Nuxt, Vue Storefront, Astro, VitePress - which explicitly position themselves as a tool for solving certain (or a wide range of) business tasks.
42 |
43 | The idea for a tool similar to `create-vue`, scaffolding a Vue 3 webapp, came up. On the one hand, a quite workable, adaptive website with the necessary functionality so that a novice developer could familiarize himself with a proven approach to solving the tasks involved. On the other hand, it should be minimalistic enough not to impose dependencies unwanted by an experienced developer and create a skeleton of established best practices for further development of the application.
44 |
45 | Clearly, defining one or another "best practice" (fetch or axios?) can be quite a moot point, but still.
46 |
--------------------------------------------------------------------------------
/.github/webapp-start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/.github/webapp-start.png
--------------------------------------------------------------------------------
/.github/workflows/gp-deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | branches: [main]
5 | workflow_dispatch:
6 | # branches: [ "main", "development" ]
7 | permissions:
8 | contents: write
9 | # pages: write
10 | jobs:
11 | build-and-deploy:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout 🛎️
15 | uses: actions/checkout@v3
16 |
17 | - uses: pnpm/action-setup@v2
18 | name: Install pnpm
19 | id: pnpm-install
20 | with:
21 | version: 8.5.0
22 | run_install: false
23 |
24 | - name: Install dependencies
25 | run: pnpm install
26 |
27 | - run: pnpm build
28 |
29 | - name: Deploy 🚀
30 | uses: JamesIves/github-pages-deploy-action@v4
31 | with:
32 | folder: dist
33 | branch: gh-pages
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | node_modules
6 | dist
7 | *.local
8 |
9 | # Editor directories and files
10 | #.vscode/*
11 | .idea
12 | .DS_Store
13 |
14 | .history
15 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | trailingComma: "es5"
2 | semi: true
3 | singleQuote: false
4 | printWidth: 110
--------------------------------------------------------------------------------
/.vscode/settings-create-vwa.json:
--------------------------------------------------------------------------------
1 | {
2 | // Enable the ESlint flat config support
3 | "eslint.experimental.useFlatConfig": true,
4 | // Disable the default formatter, use eslint instead
5 | "prettier.enable": false,
6 | "editor.formatOnSave": false,
7 | // Auto fix
8 | "editor.codeActionsOnSave": {
9 | "source.fixAll.eslint": "explicit",
10 | "source.organizeImports": "never"
11 | },
12 | // Enable eslint for all supported languages
13 | "eslint.validate": [
14 | "javascript",
15 | "javascriptreact",
16 | "typescript",
17 | "typescriptreact",
18 | "vue",
19 | "html",
20 | "markdown",
21 | "json",
22 | "jsonc",
23 | "yaml"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // {
2 | {
3 | // "i18n-ally.localesPaths": ["src/resources/lang"],
4 | // "i18n-ally.keystyle": "nested",
5 | // "cSpell.words": ["Splide"],
6 | // "editor.codeActionsOnSave": {
7 | // "source.fixAll.eslint": true,
8 | // "source.fixAll": false
9 | // },
10 | // "editor.formatOnSave": false,
11 | "workbench.colorTheme": "Default Dark Modern",
12 | // "workbench.editor.tabCloseButton": "left",
13 | "workbench.fontAliasing": "antialiased",
14 | // "workbench.iconTheme": "file-icons",
15 | "workbench.list.smoothScrolling": true,
16 | // "workbench.preferredDarkColorTheme": "Vitesse Dark",
17 | // "workbench.preferredLightColorTheme": "Vitesse Light",
18 | // "workbench.productIconTheme": "icons-carbon",
19 | // "workbench.sideBar.location": "right",
20 | // "workbench.startupEditor": "newUntitledFile",
21 | "workbench.tree.expandMode": "singleClick",
22 | "workbench.tree.indent": 10,
23 | "editor.fontFamily": "Fira Code, Input Mono, monospace",
24 | "editor.fontLigatures": "'ss01', 'ss02', 'ss03', 'ss06', 'zero'",
25 | // Editor
26 | "editor.accessibilitySupport": "off",
27 | "editor.cursorSmoothCaretAnimation": "on",
28 | "editor.find.addExtraSpaceOnTop": false,
29 | "editor.guides.bracketPairs": "active",
30 | "editor.inlineSuggest.enabled": true,
31 | // "editor.lineNumbers": "interval",
32 | // "editor.multiCursorModifier": "ctrlCmd",
33 | "editor.renderWhitespace": "boundary",
34 | "editor.suggestSelection": "first",
35 | "editor.tabSize": 2,
36 | "editor.unicodeHighlight.invisibleCharacters": false,
37 | "editor.stickyScroll.enabled": true,
38 | "editor.hover.sticky": true,
39 | "editor.wordSeparators": "-`~!@#%^&*()=+[{]}\\|;:'\",.<>/?",
40 | // "explorer.confirmDelete": false,
41 | // "explorer.confirmDragAndDrop": false,
42 | "window.autoDetectColorScheme": false,
43 | "window.dialogStyle": "custom",
44 | "window.nativeTabs": true, // this is great, macOS only
45 | "window.titleBarStyle": "custom",
46 | "workbench.editor.closeOnFileDelete": true,
47 | "workbench.editor.highlightModifiedTabs": true,
48 | "workbench.editor.limit.enabled": true,
49 | "workbench.editor.limit.perEditorGroup": true,
50 | "workbench.editor.limit.value": 30,
51 | "extensions.autoUpdate": "onlyEnabledExtensions",
52 | "extensions.ignoreRecommendations": true,
53 | "files.eol": "\n",
54 | "files.insertFinalNewline": true,
55 | "files.simpleDialog.enable": true,
56 | "git.autofetch": false,
57 | "git.confirmSync": false,
58 | "git.enableSmartCommit": true,
59 | "git.untrackedChanges": "separate",
60 | "terminal.integrated.cursorBlinking": true,
61 | "terminal.integrated.cursorStyle": "line",
62 | "terminal.integrated.fontWeight": "300",
63 | "terminal.integrated.persistentSessionReviveProcess": "never",
64 | "terminal.integrated.tabs.enabled": true,
65 | "scm.diffDecorationsGutterWidth": 2,
66 | "debug.onTaskErrors": "debugAnyway",
67 | "diffEditor.ignoreTrimWhitespace": false,
68 | "search.exclude": {
69 | "**/.git": true,
70 | "**/.github": true,
71 | "**/.nuxt": true,
72 | "**/.output": true,
73 | "**/.pnpm": true,
74 | "**/.vscode": true,
75 | "**/.yarn": true,
76 | "**/bower_components": true,
77 | "**/dist/**": true,
78 | "**/logs": true,
79 | "**/node_modules": true,
80 | "**/out/**": true,
81 | "**/package-lock.json": true,
82 | "**/pnpm-lock.yaml": true,
83 | "**/tmp": true,
84 | "**/yarn.lock": true
85 | },
86 | // Extension configs
87 | "emmet.showSuggestionsAsSnippets": true,
88 | "emmet.triggerExpansionOnTab": false,
89 | // ESLint config: https://github.com/antfu/eslint-config
90 | "eslint.codeAction.showDocumentation": {
91 | "enable": true
92 | },
93 | "eslint.quiet": false,
94 | "cSpell.allowCompoundWords": true,
95 | "cSpell.language": "en,en-US,ru,ru-RU",
96 | "css.lint.hexColorLength": "ignore",
97 | "githubIssues.workingIssueFormatScm": "#${issueNumberLabel}",
98 | "githubPullRequests.fileListLayout": "tree",
99 | "gitlens.codeLens.authors.enabled": false,
100 | "gitlens.codeLens.enabled": false,
101 | "gitlens.codeLens.recentChange.enabled": false,
102 | "gitlens.menus": {
103 | "editor": {
104 | "blame": false,
105 | "clipboard": true,
106 | "compare": true,
107 | "history": false,
108 | "remote": false
109 | },
110 | "editorGroup": {
111 | "blame": true,
112 | "compare": false
113 | },
114 | "editorTab": {
115 | "clipboard": true,
116 | "compare": true,
117 | "history": true,
118 | "remote": true
119 | },
120 | "explorer": {
121 | "clipboard": true,
122 | "compare": true,
123 | "history": true,
124 | "remote": true
125 | },
126 | "scm": {
127 | "authors": true
128 | },
129 | "scmGroup": {
130 | "compare": true,
131 | "openClose": true,
132 | "stash": true
133 | },
134 | "scmGroupInline": {
135 | "stash": true
136 | },
137 | "scmItem": {
138 | "clipboard": true,
139 | "compare": true,
140 | "history": true,
141 | "remote": false,
142 | "stash": true
143 | }
144 | },
145 | "i18n-ally.autoDetection": false,
146 | "i18n-ally.displayLanguage": "en",
147 | "i18n-ally.ignoredLocales": [],
148 | "iconify.annotations": true,
149 | "iconify.inplace": true,
150 | "svg.preview.mode": "svg",
151 | // I only use Prettier for manually formatting
152 | // "prettier.enable": true,
153 | // "prettier.printWidth": 200,
154 | // "prettier.semi": false,
155 | // "prettier.singleQuote": true,
156 | "volar.autoCompleteRefs": false,
157 | "volar.codeLens.pugTools": false,
158 | "volar.completion.preferredTagNameCase": "pascal",
159 | // File Nesting
160 | // this might not be up to date with the repo, please check yourself
161 | // https://github.com/antfu/vscode-file-nesting-config
162 | "explorer.fileNesting.enabled": true,
163 | "explorer.fileNesting.expand": false,
164 | "explorer.fileNesting.patterns": {
165 | "*.asax": "$(capture).*.cs, $(capture).*.vb",
166 | "*.ascx": "$(capture).*.cs, $(capture).*.vb",
167 | "*.ashx": "$(capture).*.cs, $(capture).*.vb",
168 | "*.aspx": "$(capture).*.cs, $(capture).*.vb",
169 | "*.bloc.dart": "$(capture).event.dart, $(capture).state.dart",
170 | "*.c": "$(capture).h",
171 | "*.cc": "$(capture).hpp, $(capture).h, $(capture).hxx",
172 | "*.cjs": "$(capture).cjs.map, $(capture).*.cjs, $(capture)_*.cjs",
173 | "*.component.ts": "$(capture).component.html, $(capture).component.spec.ts, $(capture).component.css, $(capture).component.scss, $(capture).component.sass, $(capture).component.less",
174 | "*.cpp": "$(capture).hpp, $(capture).h, $(capture).hxx",
175 | "*.cs": "$(capture).*.cs",
176 | "*.cshtml": "$(capture).cshtml.cs",
177 | "*.csproj": "*.config, *proj.user, appsettings.*, bundleconfig.json",
178 | "*.css": "$(capture).css.map, $(capture).*.css",
179 | "*.cxx": "$(capture).hpp, $(capture).h, $(capture).hxx",
180 | "*.dart": "$(capture).freezed.dart, $(capture).g.dart",
181 | "*.ex": "$(capture).html.eex, $(capture).html.heex, $(capture).html.leex",
182 | "*.go": "$(capture)_test.go",
183 | "*.java": "$(capture).class",
184 | "*.js": "$(capture).js.map, $(capture).*.js, $(capture)_*.js",
185 | "*.jsx": "$(capture).js, $(capture).*.jsx, $(capture)_*.js, $(capture)_*.jsx",
186 | "*.master": "$(capture).*.cs, $(capture).*.vb",
187 | "*.mjs": "$(capture).mjs.map, $(capture).*.mjs, $(capture)_*.mjs",
188 | "*.module.ts": "$(capture).resolver.ts, $(capture).controller.ts, $(capture).service.ts",
189 | "*.pubxml": "$(capture).pubxml.user",
190 | "*.resx": "$(capture).*.resx, $(capture).designer.cs, $(capture).designer.vb",
191 | "*.tex": "$(capture).acn, $(capture).acr, $(capture).alg, $(capture).aux, $(capture).bbl, $(capture).blg, $(capture).fdb_latexmk, $(capture).fls, $(capture).glg, $(capture).glo, $(capture).gls, $(capture).idx, $(capture).ind, $(capture).ist, $(capture).lof, $(capture).log, $(capture).lot, $(capture).out, $(capture).pdf, $(capture).synctex.gz, $(capture).toc, $(capture).xdv",
192 | "*.ts": "$(capture).js, $(capture).d.ts.map, $(capture).*.ts, $(capture)_*.js, $(capture)_*.ts",
193 | "*.tsx": "$(capture).ts, $(capture).*.tsx, $(capture)_*.ts, $(capture)_*.tsx",
194 | "*.vbproj": "*.config, *proj.user, appsettings.*, bundleconfig.json",
195 | "*.vue": "$(capture).*.ts, $(capture).*.js, $(capture).story.vue",
196 | "*.xaml": "$(capture).xaml.cs",
197 | "+layout.svelte": "+layout.ts,+layout.ts,+layout.js,+layout.server.ts,+layout.server.js,+layout.gql",
198 | "+page.svelte": "+page.server.ts,+page.server.js,+page.ts,+page.js,+page.gql",
199 | ".clang-tidy": ".clang-format, .clangd, compile_commands.json",
200 | ".env": "*.env, .env.*, .envrc, env.d.ts",
201 | ".gitignore": ".gitattributes, .gitmodules, .gitmessage, .mailmap, .git-blame*",
202 | ".project": ".classpath",
203 | "//": "Last update at 4/29/2023, 2:04:58 PM",
204 | "BUILD.bazel": "*.bzl, *.bazel, *.bazelrc, bazel.rc, .bazelignore, .bazelproject, WORKSPACE",
205 | "CMakeLists.txt": "*.cmake, *.cmake.in, .cmake-format.yaml, CMakePresets.json",
206 | "I*.cs": "$(capture).cs",
207 | "artisan": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, server.php, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, webpack.mix.js, windi.config.*",
208 | "astro.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
209 | "cargo.toml": ".clippy.toml, .rustfmt.toml, cargo.lock, clippy.toml, cross.toml, rust-toolchain.toml, rustfmt.toml",
210 | "composer.json": ".php*.cache, composer.lock, phpunit.xml*, psalm*.xml",
211 | "default.nix": "shell.nix",
212 | "deno.json*": "*.env, .env.*, .envrc, api-extractor.json, deno.lock, env.d.ts, import-map.json, import_map.json, jsconfig.*, tsconfig.*, tsdoc.*",
213 | "dockerfile": ".dockerignore, docker-compose.*, dockerfile*",
214 | "flake.nix": "flake.lock",
215 | "gatsby-config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, gatsby-browser.*, gatsby-node.*, gatsby-ssr.*, gatsby-transformer.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
216 | "gemfile": ".ruby-version, gemfile.lock",
217 | "go.mod": ".air*, go.sum",
218 | "go.work": "go.work.sum",
219 | "mix.exs": ".credo.exs, .dialyzer_ignore.exs, .formatter.exs, .iex.exs, .tool-versions, mix.lock",
220 | "next.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, next-env.d.ts, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
221 | "nuxt.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
222 | "package.json": ".browserslist*, .circleci*, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lintstagedrc*, .markdownlint*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .simple-git-hooks*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, apollo.config.*, appveyor*, azure-pipelines*, bower.json, build.config.*, commitlint*, crowdin*, dangerfile*, dlint.json, dprint.json, eslint*, firebase.json, grunt*, gulp*, jenkins*, lerna*, lint-staged*, nest-cli.*, netlify*, nodemon*, npm-shrinkwrap.json, nx.*, package-lock.json, package.nls*.json, phpcs.xml, pm2.*, pnpm*, prettier*, pullapprove*, pyrightconfig.json, release-tasks.sh, release.config.*, renovate*, rollup.config.*, rspack*, simple-git-hooks*, stylelint*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, webpack*, workspace.json, xo.config.*, yarn*",
223 | "pubspec.yaml": ".metadata, .packages, all_lint_rules.yaml, analysis_options.yaml, build.yaml, pubspec.lock, pubspec_overrides.yaml",
224 | "pyproject.toml": ".pdm.toml, pdm.lock, pyproject.toml",
225 | "quasar.conf.js": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, quasar.extensions.json, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
226 | "readme*": "authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying, credits, governance.md, history.md, license*, maintainers, readme*, security.md, sponsors*",
227 | "remix.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, remix.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
228 | "rush.json": ".browserslist*, .circleci*, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lintstagedrc*, .markdownlint*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .simple-git-hooks*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, apollo.config.*, appveyor*, azure-pipelines*, bower.json, build.config.*, commitlint*, crowdin*, dangerfile*, dlint.json, dprint.json, eslint*, firebase.json, grunt*, gulp*, jenkins*, lerna*, lint-staged*, nest-cli.*, netlify*, nodemon*, npm-shrinkwrap.json, nx.*, package-lock.json, package.nls*.json, phpcs.xml, pm2.*, pnpm*, prettier*, pullapprove*, pyrightconfig.json, release-tasks.sh, release.config.*, renovate*, rollup.config.*, rspack*, simple-git-hooks*, stylelint*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, webpack*, workspace.json, xo.config.*, yarn*",
229 | "shims.d.ts": "*.d.ts",
230 | "svelte.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, houdini.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, mdsvex.config.js, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vite.config.*, vitest.config.*, webpack.config.*, windi.config.*",
231 | "vite.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*",
232 | "vue.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, playwright.config.*, postcss.config.*, puppeteer.config.*, rspack.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, webpack.config.*, windi.config.*"
233 | },
234 | // Enable the ESlint flat config support
235 | "eslint.experimental.useFlatConfig": true,
236 | // Disable the default formatter, use eslint instead
237 | "prettier.enable": false,
238 | "editor.formatOnSave": false,
239 | // Auto fix
240 | "editor.codeActionsOnSave": {
241 | "source.fixAll.eslint": "explicit",
242 | "source.organizeImports": "never"
243 | },
244 |
245 | "eslint.workingDirectories": [
246 | { "pattern": "./src/*/" },
247 | { "pattern": "./*" }
248 | ],
249 | // Silent the stylistic rules in you IDE, but still auto fix them
250 | "eslint.rules.customizations": [
251 | {
252 | "rule": "style/*",
253 | "severity": "off"
254 | },
255 | {
256 | "rule": "*-indent",
257 | "severity": "off"
258 | },
259 | {
260 | "rule": "*-spacing",
261 | "severity": "off"
262 | },
263 | {
264 | "rule": "*-spaces",
265 | "severity": "off"
266 | },
267 | {
268 | "rule": "*-order",
269 | "severity": "off"
270 | },
271 | {
272 | "rule": "*-dangle",
273 | "severity": "off"
274 | },
275 | {
276 | "rule": "*-newline",
277 | "severity": "off"
278 | },
279 | {
280 | "rule": "*quotes",
281 | "severity": "off"
282 | },
283 | {
284 | "rule": "*semi",
285 | "severity": "off"
286 | }
287 | ],
288 | // Enable eslint for all supported languages
289 | "eslint.validate": [
290 | "javascript",
291 | "javascriptreact",
292 | "typescript",
293 | "typescriptreact",
294 | "vue",
295 | "html",
296 | "markdown",
297 | "json",
298 | "jsonc",
299 | "yaml"
300 | ]
301 | }
302 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue webapp template
2 |
3 |
4 | Empty NPM package for the purpose of existence of `create-vue-webapp` package.
5 |
6 | Full documentation is available at [https://vue-faq.org/en/vue-webapp/](https://vue-faq.org/en/vue-webapp/)
7 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import antfu from "@antfu/eslint-config";
2 |
3 | export default antfu({
4 | ignores: [".vscode/settings.json", ".vscode/settings.json/**", "src/assets/locales/*.json", "src/assets/locales/*.json/**"],
5 | rules: {
6 | "ts/semi": "off",
7 | "curly": ["error", "all"],
8 | "no-console": "off",
9 | "no-alert": "off",
10 | "vue/html-self-closing": "off",
11 | "style/semi": ["error", "always"],
12 | "style/indent": 2, // 4, or 'tab'
13 | "style/quotes": [
14 | "error",
15 | "double",
16 | ],
17 | "style/brace-style": ["error", "1tbs", { allowSingleLine: true }],
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-webapp",
3 | "type": "module",
4 | "version": "1.0.6",
5 | "description": "A basic template for building a new Vue 3 web application using the latest technologies and best practices",
6 | "license": "MIT",
7 | "homepage": "https://github.com/vuesence/vue-webapp",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/vuesence/vue-webapp.git"
11 | },
12 | "bugs": {
13 | "url": "https://github.com/vuesence/vue-webapp/issues"
14 | },
15 | "keywords": [
16 | "vue",
17 | "vite",
18 | "website",
19 | "webapp",
20 | "template",
21 | "scaffold"
22 | ],
23 | "scripts": {
24 | "dev": "vite",
25 | "build": "vite build",
26 | "build:tsc": "vue-tsc && vite build",
27 | "preview": "vite preview",
28 | "lint": "eslint .",
29 | "lint:fix": "eslint . --fix"
30 | },
31 | "dependencies": {
32 | "vue": "^3.4.33",
33 | "vue-router": "^4.4.0"
34 | },
35 | "devDependencies": {
36 | "@antfu/eslint-config": "^2.23.2",
37 | "@types/node": "^20.14.11",
38 | "@vitejs/plugin-vue": "^5.0.5",
39 | "eslint": "^9.7.0",
40 | "typescript": "^5.5.4",
41 | "vite": "^5.3.4",
42 | "vite-plugin-html-injection": "^1.4.2"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/assets/images/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/assets/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/favicon-32x32.png
--------------------------------------------------------------------------------
/public/assets/images/logo-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/logo-128.png
--------------------------------------------------------------------------------
/public/assets/images/logo-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/logo-48.png
--------------------------------------------------------------------------------
/public/assets/images/logo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/logo-512.png
--------------------------------------------------------------------------------
/public/assets/images/logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/logo-small.png
--------------------------------------------------------------------------------
/public/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/assets/images/logo.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/public/favicon.ico
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Acme Corporation webapp",
3 | "dir": "auto",
4 | "display": "standalone",
5 | "name": "Acme Inc.",
6 | "orientation": "any",
7 | "scope": "/",
8 | "short_name": "Acme",
9 | "start_url": "/",
10 | "categories": [
11 | "it",
12 | "development",
13 | "education"
14 | ],
15 | "icons": [
16 | {
17 | "src": "/assets/images/logo-512.png",
18 | "type": "image/png",
19 | "sizes": "512x512",
20 | "purpose": "any maskable"
21 | },
22 | {
23 | "src": "/assets/images/logo-48.png",
24 | "type": "image/png",
25 | "sizes": "48x48",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "/assets/images/logo-128.png",
30 | "type": "image/png",
31 | "sizes": "128x128",
32 | "purpose": "maskable"
33 | },
34 | {
35 | "src": "/favicon.ico",
36 | "type": "image/x-icon"
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/public/service-worker.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-restricted-globals */
2 | const VERSION = 1.0;
3 | // const HOSTNAME = self.location.hostname;
4 |
5 | const nonCached = ["/", "/build.json"];
6 |
7 | const CHACHE_LIST = {
8 | assets: `assets-v${VERSION}`,
9 | images: `images-v${VERSION}`,
10 | fonts: `fonts-v${VERSION}`,
11 | };
12 |
13 | self.addEventListener("install", () => {
14 | self.skipWaiting();
15 | console.log("Service Worker has been installed");
16 | });
17 |
18 | self.addEventListener("activate", (event) => {
19 | const expectedCacheNames = Object.keys(CHACHE_LIST).map((key) => {
20 | return CHACHE_LIST[key];
21 | });
22 |
23 | // Delete out of date caches
24 | event.waitUntil(
25 | caches.keys().then((cacheNames) => {
26 | return Promise.all(
27 | cacheNames.map((cacheName) => {
28 | if (!expectedCacheNames.includes(cacheName)) {
29 | console.log("Deleting out of date cache:", cacheName);
30 | return caches.delete(cacheName);
31 | }
32 | return null;
33 | }),
34 | );
35 | }),
36 | );
37 | console.log("Service Worker has been activated");
38 | });
39 |
40 | self.addEventListener("fetch", (event) => {
41 | // console.log("Fetching:", event.request.url);
42 | event.respondWith(
43 | (async function () {
44 | // console.log(event.request);
45 | const cachedResponse = await caches.match(event.request);
46 | if (cachedResponse) {
47 | // console.log("\tCached version found: " + event.request.url);
48 | return cachedResponse;
49 | } else {
50 | console.log(`\tGetting from the Internet:${event.request.url}`);
51 | return await fetchAndCache(event.request);
52 | }
53 | })(),
54 | );
55 | });
56 |
57 | function fetchAndCache(request) {
58 | return fetch(request)
59 | .then((response) => {
60 | // Check if we received a valid response
61 | if (!response.ok) {
62 | return response;
63 | }
64 | // throw Error(response.statusText);
65 |
66 | const url = new URL(request.url);
67 | // console.log(url);
68 | if (
69 | response.status < 400
70 | && response.type === "basic"
71 | && !url.search.includes("mode=nocache")
72 | && !nonCached.includes(url.pathname)
73 | && url.protocol !== "chrome-extension:"
74 | ) {
75 | const cur_cache = getCache(response);
76 | if (cur_cache) {
77 | // console.log("\tCaching the response to", request.url);
78 | return caches.open(cur_cache).then((cache) => {
79 | cache.put(request, response.clone());
80 | return response;
81 | });
82 | }
83 | }
84 | return response;
85 | })
86 | .catch((error) => {
87 | console.log(`Request failed for: ${request.url}`, error);
88 | throw error;
89 | });
90 | }
91 |
92 | function getCache(response) {
93 | const contentType = response.headers.get("content-type");
94 | if (contentType) {
95 | if (contentType.includes("image")) {
96 | return CHACHE_LIST.images;
97 | } else if (
98 | ["application/javascript", "text/css", "font/woff2"].includes(contentType)
99 | ) { return CHACHE_LIST.assets; }
100 | }
101 | return false;
102 | }
103 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
22 |
--------------------------------------------------------------------------------
/src/assets/images/about.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/images/change-account.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/images/contact-us.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/images/hamburger.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/home.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/images/icon-plus-blue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/icon-plus-purple.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/instagram.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/assets/images/logo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuesence/vue-webapp/f341029e21b8bbc4839962e67188afc1857c6b7f/src/assets/images/logo.webp
--------------------------------------------------------------------------------
/src/assets/images/logo1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/assets/images/logout.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/assets/images/moon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/sun.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/images/youtube.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/styles/base.css:
--------------------------------------------------------------------------------
1 | @import "./vars.css";
2 | /* // @import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@500&display=swap");
3 | // @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); */
4 |
5 | :root {
6 | /* // font-family: 'Roboto', sans-serif;
7 | // font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
8 | // line-height: 1.5;
9 | // font-weight: 400;
10 |
11 | // color-scheme: light dark;
12 | // color: #213547;
13 | // background-color: #ffffff;
14 |
15 | // font-synthesis: none;
16 | // text-rendering: optimizeLegibility;
17 | // -webkit-font-smoothing: antialiased;
18 | // -moz-osx-font-smoothing: grayscale;
19 | // -webkit-text-size-adjust: 100%; */
20 | }
21 |
22 | @media screen and (min-width: 960px) {
23 | html {
24 | overflow-x: hidden;
25 | margin-right: calc(-1 * (100vw - 100%));
26 | }
27 | }
28 |
29 |
30 | *,
31 | ::before,
32 | ::after {
33 | box-sizing: border-box;
34 | }
35 |
36 | html {
37 | line-height: 1.4;
38 | font-size: 16px;
39 | -webkit-text-size-adjust: 100%;
40 | }
41 |
42 | html.dark {
43 | color-scheme: dark;
44 | }
45 |
46 | body {
47 | /* // margin: 0; */
48 | display: flex;
49 | place-items: center;
50 | min-width: 320px;
51 | /* // background-color: white;
52 | // min-height: 100vh; */
53 | margin: 0;
54 | width: 100%;
55 | min-width: 320px;
56 | min-height: 100vh;
57 | line-height: 24px;
58 | font-family: var(--vwa-font-family-base);
59 | font-size: 16px;
60 | font-weight: 400;
61 | color: var(--vwa-c-text-1);
62 | background-color: var(--vwa-c-bg);
63 | direction: ltr;
64 | font-synthesis: style;
65 | text-rendering: optimizeLegibility;
66 | -webkit-font-smoothing: antialiased;
67 | -moz-osx-font-smoothing: grayscale;
68 | }
69 |
70 | a {
71 | /* // font-weight: 500;
72 | // color: #646cff; */
73 | color: inherit;
74 | text-decoration: inherit;
75 | transition: color .25s;
76 | /* // &:hover {
77 | // color: #535bf2;
78 | // } */
79 | }
80 |
81 | ol,
82 | ul {
83 | list-style: none;
84 | margin: 0;
85 | padding: 0;
86 | }
87 |
88 | img,
89 | svg,
90 | video,
91 | canvas,
92 | audio,
93 | iframe,
94 | embed,
95 | object {
96 | display: block;
97 | }
98 | img,
99 | video {
100 | max-width: 100%;
101 | height: auto;
102 | }
103 |
104 |
105 | button,
106 | input,
107 | optgroup,
108 | select,
109 | textarea {
110 | border: 1px solid var(--vwa-c-border);
111 | padding: 0;
112 | line-height: inherit;
113 | color: inherit;
114 | }
115 |
116 | button {
117 | padding: 0;
118 | font-family: inherit;
119 | background-color: transparent;
120 | background-image: none;
121 | }
122 |
123 | button:enabled,
124 | [role='button']:enabled {
125 | cursor: pointer;
126 | }
127 |
128 | button:focus,
129 | button:focus-visible {
130 | outline: 1px dotted;
131 | outline: 4px auto -webkit-focus-ring-color;
132 | }
133 |
134 | button:focus:not(:focus-visible) {
135 | outline: none !important;
136 | }
137 |
138 | input:focus,
139 | textarea:focus,
140 | select:focus {
141 | outline: none;
142 | }
143 |
144 | input, textarea {
145 | background-color: transparent;
146 | }
147 | input:-ms-input-placeholder,
148 | textarea:-ms-input-placeholder {
149 | color: var(--vwa-c-text-3);
150 | }
151 |
152 | input::-ms-input-placeholder,
153 | textarea::-ms-input-placeholder {
154 | color: var(--vwa-c-text-3);
155 | }
156 |
157 | input::placeholder,
158 | textarea::placeholder {
159 | color: var(--vwa-c-text-3);
160 | }
161 |
162 | input::-webkit-outer-spin-button,
163 | input::-webkit-inner-spin-button {
164 | -webkit-appearance: none;
165 | margin: 0;
166 | }
167 |
168 | input[type='number'] {
169 | -moz-appearance: textfield;
170 | }
171 | select {
172 | -webkit-appearance: none;
173 | }
174 |
175 | #app {
176 | /* // min-height: 100vh; */
177 | width: var(--vwa-layout-max-width);
178 | max-width: var(--vwa-layout-max-width);
179 | margin: 0 auto;
180 | /* // padding: 2em 2em 0;
181 | // text-align: center; */
182 | }
183 |
--------------------------------------------------------------------------------
/src/assets/styles/custom.css:
--------------------------------------------------------------------------------
1 |
2 | // layout-placeholder
3 |
--------------------------------------------------------------------------------
/src/assets/styles/vars.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Typography
3 | * -------------------------------------------------------------------------- */
4 |
5 | @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
6 |
7 | :root {
8 | --vwa-font-family-base: 'Roboto', sans-serif;
9 | --vwa-font-family-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco,
10 | Consolas, 'Liberation Mono', 'Courier New', monospace;
11 | }
12 |
13 |
14 | /**
15 | * Colors: Solid
16 | * -------------------------------------------------------------------------- */
17 |
18 | :root {
19 | --vwa-c-white: #ffffff;
20 | --vwa-c-black: #000000;
21 |
22 | --vwa-c-neutral: var(--vwa-c-black);
23 | --vwa-c-neutral-inverse: var(--vwa-c-white);
24 | }
25 |
26 | .dark {
27 | --vwa-c-neutral: var(--vwa-c-white);
28 | --vwa-c-neutral-inverse: var(--vwa-c-black);
29 | }
30 |
31 | /**
32 | * Colors: Palette
33 | *
34 | * The primitive colors used for accent colors. These colors are referenced
35 | * by functional colors such as "Text", "Background", or "Brand".
36 | *
37 | * Each colors have exact same color scale system with 3 levels of solid
38 | * colors with different brightness, and 1 soft color.
39 | *
40 | * - `XXX-1`: The most solid color used mainly for colored text. It must
41 | * satisfy the contrast ratio against when used on top of `XXX-soft`.
42 | *
43 | * - `XXX-2`: The color used mainly for hover state of the button.
44 | *
45 | * - `XXX-3`: The color for solid background, such as bg color of the button.
46 | * It must satisfy the contrast ratio with pure white (#ffffff) text on
47 | * top of it.
48 | *
49 | * - `XXX-soft`: The color used for subtle background such as custom container
50 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
51 | * on top of it.
52 | *
53 | * The soft color must be semi transparent alpha channel. This is crucial
54 | * because it allows adding multiple "soft" colors on top of each other
55 | * to create a accent, such as when having inline code block inside
56 | * custom containers.
57 | * -------------------------------------------------------------------------- */
58 |
59 | :root {
60 | --vwa-c-gray-1: #dddde3;
61 | --vwa-c-gray-2: #e4e4e9;
62 | --vwa-c-gray-3: #ebebef;
63 | --vwa-c-gray-soft: rgba(142, 150, 170, 0.14);
64 |
65 | --vwa-c-indigo-1: #3451b2;
66 | --vwa-c-indigo-2: #3a5ccc;
67 | --vwa-c-indigo-3: #5672cd;
68 | --vwa-c-indigo-soft: rgba(100, 108, 255, 0.14);
69 |
70 | --vwa-c-green-1: #18794e;
71 | --vwa-c-green-2: #299764;
72 | --vwa-c-green-3: #30a46c;
73 | --vwa-c-green-soft: rgba(16, 185, 129, 0.14);
74 |
75 | --vwa-c-yellow-1: #915930;
76 | --vwa-c-yellow-2: #946300;
77 | --vwa-c-yellow-3: #9f6a00;
78 | --vwa-c-yellow-soft: rgba(234, 179, 8, 0.14);
79 |
80 | --vwa-c-red-1: #b8272c;
81 | --vwa-c-red-2: #d5393e;
82 | --vwa-c-red-3: #e0575b;
83 | --vwa-c-red-soft: rgba(244, 63, 94, 0.14);
84 |
85 | --vwa-c-sponsor: #db2777;
86 | }
87 |
88 | .dark {
89 | --vwa-c-gray-1: #515c67;
90 | --vwa-c-gray-2: #414853;
91 | --vwa-c-gray-3: #32363f;
92 | --vwa-c-gray-soft: rgba(101, 117, 133, 0.16);
93 |
94 | --vwa-c-indigo-1: #a8b1ff;
95 | --vwa-c-indigo-2: #5c73e7;
96 | --vwa-c-indigo-3: #3e63dd;
97 | --vwa-c-indigo-soft: rgba(100, 108, 255, 0.16);
98 |
99 | --vwa-c-green-1: #3dd68c;
100 | --vwa-c-green-2: #30a46c;
101 | --vwa-c-green-3: #298459;
102 | --vwa-c-green-soft: rgba(16, 185, 129, 0.16);
103 |
104 | --vwa-c-yellow-1: #f9b44e;
105 | --vwa-c-yellow-2: #da8b17;
106 | --vwa-c-yellow-3: #a46a0a;
107 | --vwa-c-yellow-soft: rgba(234, 179, 8, 0.16);
108 |
109 | --vwa-c-red-1: #f66f81;
110 | --vwa-c-red-2: #f14158;
111 | --vwa-c-red-3: #b62a3c;
112 | --vwa-c-red-soft: rgba(244, 63, 94, 0.16);
113 | }
114 |
115 | /**
116 | * Colors: Background
117 | *
118 | * - `bg`: The bg color used for main screen.
119 | *
120 | * - `bg-alt`: The alternative bg color used in places such as "sidebar",
121 | * or "code block".
122 | *
123 | * - `bg-elv`: The elevated bg color. This is used at parts where it "floats",
124 | * such as "dialog".
125 | *
126 | * - `bg-soft`: The bg color to slightly distinguish some components from
127 | * the page. Used for things like "carbon ads" or "table".
128 | * -------------------------------------------------------------------------- */
129 |
130 | :root {
131 | --vwa-c-bg: #ffffff;
132 | --vwa-c-bg-alt: #f6f6f7;
133 | --vwa-c-bg-elv: #ffffff;
134 | --vwa-c-bg-soft: #f6f6f7;
135 | }
136 |
137 | .dark {
138 | --vwa-c-bg: #1b1b1f;
139 | --vwa-c-bg-alt: #161618;
140 | --vwa-c-bg-elv: #202127;
141 | --vwa-c-bg-soft: #202127;
142 | }
143 |
144 | /**
145 | * Colors: Borders
146 | *
147 | * - `divider`: This is used for separators. This is used to divide sections
148 | * within the same components, such as having separator on "h2" heading.
149 | *
150 | * - `border`: This is designed for borders on interactive components.
151 | * For example this should be used for a button outline.
152 | *
153 | * - `gutter`: This is used to divide components in the page. For example
154 | * the header and the lest of the page.
155 | * -------------------------------------------------------------------------- */
156 |
157 | :root {
158 | --vwa-c-border: #c2c2c4;
159 | --vwa-c-divider: #e2e2e3;
160 | --vwa-c-gutter: #e2e2e3;
161 | }
162 |
163 | .dark {
164 | --vwa-c-border: #3c3f44;
165 | --vwa-c-divider: #2e2e32;
166 | --vwa-c-gutter: #000000;
167 | }
168 |
169 | /**
170 | * Colors: Text
171 | *
172 | * - `text-1`: Used for primary text.
173 | *
174 | * - `text-2`: Used for muted texts, such as "inactive menu" or "info texts".
175 | *
176 | * - `text-3`: Used for subtle texts, such as "placeholders" or "caret icon".
177 | * -------------------------------------------------------------------------- */
178 |
179 | :root {
180 | --vwa-c-text-1: rgba(60, 60, 67);
181 | --vwa-c-text-2: rgba(60, 60, 67, 0.78);
182 | --vwa-c-text-3: rgba(60, 60, 67, 0.56);
183 | }
184 |
185 | .dark {
186 | --vwa-c-text-1: rgba(255, 255, 245, 0.86);
187 | --vwa-c-text-2: rgba(235, 235, 245, 0.6);
188 | --vwa-c-text-3: rgba(235, 235, 245, 0.38);
189 | }
190 |
191 | /**
192 | * Colors: Function
193 | *
194 | * - `default`: The color used purely for subtle indication without any
195 | * special meanings attached to it such as bg color for menu hover state.
196 | *
197 | * - `brand`: Used for primary brand colors, such as link text, button with
198 | * brand theme, etc.
199 | *
200 | * - `tip`: Used to indicate useful information. The default theme uses the
201 | * brand color for this by default.
202 | *
203 | * - `warning`: Used to indicate warning to the users. Used in custom
204 | * container, badges, etc.
205 | *
206 | * - `danger`: Used to show error, or dangerous message to the users. Used
207 | * in custom container, badges, etc.
208 | *
209 | * To understand the scaling system, refer to "Colors: Palette" section.
210 | * -------------------------------------------------------------------------- */
211 |
212 | :root {
213 | --vwa-c-default-1: var(--vwa-c-gray-1);
214 | --vwa-c-default-2: var(--vwa-c-gray-2);
215 | --vwa-c-default-3: var(--vwa-c-gray-3);
216 | --vwa-c-default-soft: var(--vwa-c-gray-soft);
217 |
218 | --vwa-c-brand-1: var(--vwa-c-indigo-1);
219 | --vwa-c-brand-2: var(--vwa-c-indigo-2);
220 | --vwa-c-brand-3: var(--vwa-c-indigo-3);
221 | --vwa-c-brand-soft: var(--vwa-c-indigo-soft);
222 |
223 | --vwa-c-tip-1: var(--vwa-c-brand-1);
224 | --vwa-c-tip-2: var(--vwa-c-brand-2);
225 | --vwa-c-tip-3: var(--vwa-c-brand-3);
226 | --vwa-c-tip-soft: var(--vwa-c-brand-soft);
227 |
228 | --vwa-c-warning-1: var(--vwa-c-yellow-1);
229 | --vwa-c-warning-2: var(--vwa-c-yellow-2);
230 | --vwa-c-warning-3: var(--vwa-c-yellow-3);
231 | --vwa-c-warning-soft: var(--vwa-c-yellow-soft);
232 |
233 | --vwa-c-danger-1: var(--vwa-c-red-1);
234 | --vwa-c-danger-2: var(--vwa-c-red-2);
235 | --vwa-c-danger-3: var(--vwa-c-red-3);
236 | --vwa-c-danger-soft: var(--vwa-c-red-soft);
237 | }
238 |
239 |
240 | /**
241 | * Shadows
242 | * -------------------------------------------------------------------------- */
243 |
244 | :root {
245 | --vwa-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
246 | --vwa-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07);
247 | --vwa-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08);
248 | --vwa-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12);
249 | --vwa-shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16);
250 | }
251 |
252 | /**
253 | * Z-indexes
254 | * -------------------------------------------------------------------------- */
255 |
256 | :root {
257 | --vwa-z-index-footer: 10;
258 | --vwa-z-index-local-nav: 20;
259 | --vwa-z-index-nav: 30;
260 | --vwa-z-index-layout-top: 40;
261 | --vwa-z-index-backdrop: 50;
262 | --vwa-z-index-sidebar: 60;
263 | }
264 |
265 | /**
266 | * Icons
267 | * -------------------------------------------------------------------------- */
268 |
269 | :root {
270 | --vwa-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");
271 | --vwa-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
272 | }
273 |
274 | /**
275 | * Layouts
276 | * -------------------------------------------------------------------------- */
277 |
278 | :root {
279 | --vwa-layout-max-width: 1280px;
280 | }
281 |
282 | /**
283 | * Component: Header Anchor
284 | * -------------------------------------------------------------------------- */
285 |
286 | :root {
287 | --vwa-header-anchor-symbol: '#';
288 | }
289 |
290 | /**
291 | * Component: Button
292 | * -------------------------------------------------------------------------- */
293 |
294 | :root {
295 | --vwa-button-brand-border: transparent;
296 | --vwa-button-brand-text: var(--vwa-c-white);
297 | --vwa-button-brand-bg: var(--vwa-c-brand-3);
298 | --vwa-button-brand-hover-border: transparent;
299 | --vwa-button-brand-hover-text: var(--vwa-c-white);
300 | --vwa-button-brand-hover-bg: var(--vwa-c-brand-2);
301 | --vwa-button-brand-active-border: transparent;
302 | --vwa-button-brand-active-text: var(--vwa-c-white);
303 | --vwa-button-brand-active-bg: var(--vwa-c-brand-1);
304 |
305 | --vwa-button-alt-border: transparent;
306 | --vwa-button-alt-text: var(--vwa-c-text-1);
307 | --vwa-button-alt-bg: var(--vwa-c-default-3);
308 | --vwa-button-alt-hover-border: transparent;
309 | --vwa-button-alt-hover-text: var(--vwa-c-text-1);
310 | --vwa-button-alt-hover-bg: var(--vwa-c-default-2);
311 | --vwa-button-alt-active-border: transparent;
312 | --vwa-button-alt-active-text: var(--vwa-c-text-1);
313 | --vwa-button-alt-active-bg: var(--vwa-c-default-1);
314 |
315 | --vwa-button-sponsor-border: var(--vwa-c-text-2);
316 | --vwa-button-sponsor-text: var(--vwa-c-text-2);
317 | --vwa-button-sponsor-bg: transparent;
318 | --vwa-button-sponsor-hover-border: var(--vwa-c-sponsor);
319 | --vwa-button-sponsor-hover-text: var(--vwa-c-sponsor);
320 | --vwa-button-sponsor-hover-bg: transparent;
321 | --vwa-button-sponsor-active-border: var(--vwa-c-sponsor);
322 | --vwa-button-sponsor-active-text: var(--vwa-c-sponsor);
323 | --vwa-button-sponsor-active-bg: transparent;
324 | }
325 |
326 | /**
327 | * Component: Custom Block
328 | * -------------------------------------------------------------------------- */
329 |
330 | :root {
331 | --vwa-custom-block-font-size: 14px;
332 | --vwa-custom-block-code-font-size: 13px;
333 |
334 | --vwa-custom-block-info-border: transparent;
335 | --vwa-custom-block-info-text: var(--vwa-c-text-1);
336 | --vwa-custom-block-info-bg: var(--vwa-c-default-soft);
337 | --vwa-custom-block-info-code-bg: var(--vwa-c-default-soft);
338 |
339 | --vwa-custom-block-tip-border: transparent;
340 | --vwa-custom-block-tip-text: var(--vwa-c-text-1);
341 | --vwa-custom-block-tip-bg: var(--vwa-c-brand-soft);
342 | --vwa-custom-block-tip-code-bg: var(--vwa-c-brand-soft);
343 |
344 | --vwa-custom-block-warning-border: transparent;
345 | --vwa-custom-block-warning-text: var(--vwa-c-text-1);
346 | --vwa-custom-block-warning-bg: var(--vwa-c-warning-soft);
347 | --vwa-custom-block-warning-code-bg: var(--vwa-c-warning-soft);
348 |
349 | --vwa-custom-block-danger-border: transparent;
350 | --vwa-custom-block-danger-text: var(--vwa-c-text-1);
351 | --vwa-custom-block-danger-bg: var(--vwa-c-danger-soft);
352 | --vwa-custom-block-danger-code-bg: var(--vwa-c-danger-soft);
353 |
354 | --vwa-custom-block-details-border: var(--vwa-custom-block-info-border);
355 | --vwa-custom-block-details-text: var(--vwa-custom-block-info-text);
356 | --vwa-custom-block-details-bg: var(--vwa-custom-block-info-bg);
357 | --vwa-custom-block-details-code-bg: var(--vwa-custom-block-info-code-bg);
358 | }
359 |
360 | /**
361 | * Component: Input
362 | * -------------------------------------------------------------------------- */
363 |
364 | :root {
365 | --vwa-input-border-color: var(--vwa-c-border);
366 | --vwa-input-bg-color: var(--vwa-c-bg-alt);
367 |
368 | --vwa-input-switch-bg-color: var(--vwa-c-gray-soft);
369 | }
370 |
371 | /**
372 | * Component: Nav
373 | * -------------------------------------------------------------------------- */
374 |
375 | :root {
376 | --vwa-nav-height: 64px;
377 | --vwa-nav-bg-color: var(--vwa-c-bg);
378 | --vwa-nav-screen-bg-color: var(--vwa-c-bg);
379 | --vwa-nav-logo-height: 24px;
380 | }
381 |
382 | .hide-nav {
383 | --vwa-nav-height: 0px;
384 | }
385 |
386 | .hide-nav .VPSidebar {
387 | --vwa-nav-height: 22px;
388 | }
389 |
390 | /**
391 | * Component: Local Nav
392 | * -------------------------------------------------------------------------- */
393 |
394 | :root {
395 | --vwa-local-nav-bg-color: var(--vwa-c-bg);
396 | }
397 |
398 | /**
399 | * Component: Sidebar
400 | * -------------------------------------------------------------------------- */
401 |
402 | :root {
403 | --vwa-sidebar-width: 272px;
404 | --vwa-sidebar-bg-color: var(--vwa-c-bg-alt);
405 | }
406 |
407 | /**
408 | * Colors Backdrop
409 | * -------------------------------------------------------------------------- */
410 |
411 | :root {
412 | --vwa-backdrop-bg-color: rgba(0, 0, 0, 0.6);
413 | }
414 |
415 | /**
416 | * Component: Home
417 | * -------------------------------------------------------------------------- */
418 |
419 | :root {
420 | --vwa-home-hero-name-color: var(--vwa-c-brand-1);
421 | --vwa-home-hero-name-background: transparent;
422 |
423 | --vwa-home-hero-image-background-image: none;
424 | --vwa-home-hero-image-filter: none;
425 | }
426 |
427 | /**
428 | * Component: Badge
429 | * -------------------------------------------------------------------------- */
430 |
431 | :root {
432 | --vwa-badge-info-border: transparent;
433 | --vwa-badge-info-text: var(--vwa-c-text-2);
434 | --vwa-badge-info-bg: var(--vwa-c-default-soft);
435 |
436 | --vwa-badge-tip-border: transparent;
437 | --vwa-badge-tip-text: var(--vwa-c-brand-1);
438 | --vwa-badge-tip-bg: var(--vwa-c-brand-soft);
439 |
440 | --vwa-badge-warning-border: transparent;
441 | --vwa-badge-warning-text: var(--vwa-c-warning-1);
442 | --vwa-badge-warning-bg: var(--vwa-c-warning-soft);
443 |
444 | --vwa-badge-danger-border: transparent;
445 | --vwa-badge-danger-text: var(--vwa-c-danger-1);
446 | --vwa-badge-danger-bg: var(--vwa-c-danger-soft);
447 | }
448 |
449 | /**
450 | * Component: Carbon Ads
451 | * -------------------------------------------------------------------------- */
452 |
453 | :root {
454 | --vwa-carbon-ads-text-color: var(--vwa-c-text-1);
455 | --vwa-carbon-ads-poweredby-color: var(--vwa-c-text-2);
456 | --vwa-carbon-ads-bg-color: var(--vwa-c-bg-soft);
457 | --vwa-carbon-ads-hover-text-color: var(--vwa-c-brand-1);
458 | --vwa-carbon-ads-hover-poweredby-color: var(--vwa-c-text-1);
459 | }
460 |
461 | /**
462 | * Component: Local Search
463 | * -------------------------------------------------------------------------- */
464 |
465 | :root {
466 | --vwa-local-search-bg: var(--vwa-c-bg);
467 | --vwa-local-search-result-bg: var(--vwa-c-bg);
468 | --vwa-local-search-result-border: var(--vwa-c-divider);
469 | --vwa-local-search-result-selected-bg: var(--vwa-c-bg);
470 | --vwa-local-search-result-selected-border: var(--vwa-c-brand-1);
471 | --vwa-local-search-highlight-bg: var(--vwa-c-brand-1);
472 | --vwa-local-search-highlight-text: var(--vwa-c-neutral-inverse);
473 | }
474 |
--------------------------------------------------------------------------------
/src/components/AppContentPane.vue:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
34 |
--------------------------------------------------------------------------------
/src/components/SectionHeader.vue:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
23 |
24 |
25 |
101 |
--------------------------------------------------------------------------------
/src/components/drawers/SimpleDrawer.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
9 |
13 |
14 |
15 |
16 |
63 |
--------------------------------------------------------------------------------
/src/components/drawers/TouchSlideoutDrawer.vue:
--------------------------------------------------------------------------------
1 |
209 |
210 |
211 |
218 |
223 |
224 |
225 |
284 |
--------------------------------------------------------------------------------
/src/components/footers/DistributedFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
44 |
45 |
46 |
201 |
--------------------------------------------------------------------------------
/src/components/footers/MantineRichFooter.vue:
--------------------------------------------------------------------------------
1 |
40 |
41 |
42 |
79 |
80 |
81 |
203 |
--------------------------------------------------------------------------------
/src/components/footers/MantineSimpleFooter.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
67 |
68 |
69 |
157 |
--------------------------------------------------------------------------------
/src/components/footers/RichFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
101 |
102 |
103 |
236 |
--------------------------------------------------------------------------------
/src/components/footers/SimpleFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
23 |
--------------------------------------------------------------------------------
/src/components/headers/MantineLayeredHeader.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
57 |
58 |
59 |
159 |
--------------------------------------------------------------------------------
/src/components/headers/MantineSimpleHeader.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
35 |
36 |
37 |
90 |
--------------------------------------------------------------------------------
/src/components/headers/SimpleHeader.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
17 |
18 |
19 |
41 |
--------------------------------------------------------------------------------
/src/components/headers/SlidingHeader.vue:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
38 |
39 |
40 |
68 |
--------------------------------------------------------------------------------
/src/components/navbars/MantineSimpleNavbar.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 |
37 |
49 |
50 |
51 |
52 |
105 |
--------------------------------------------------------------------------------
/src/components/navbars/SimpleNavbar.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
28 |
29 |
30 |
31 |
56 |
--------------------------------------------------------------------------------
/src/components/ui/BaseIcon.vue:
--------------------------------------------------------------------------------
1 |
43 |
44 |
45 |
58 |
69 |
70 |
71 |
85 |
--------------------------------------------------------------------------------
/src/components/ui/BaseToggle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
63 |
--------------------------------------------------------------------------------
/src/components/ui/HamburgerIcon.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
55 |
--------------------------------------------------------------------------------
/src/components/ui/ThemeToggle.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
25 |
26 |
27 |
28 |
29 |
30 |
42 |
--------------------------------------------------------------------------------
/src/composables/useAppConfig.ts:
--------------------------------------------------------------------------------
1 | import { ref, watch } from "vue";
2 |
3 | const isDrawerOpen = ref(false);
4 | const isDarkTheme = ref(false);
5 | const APPEARANCE_KEY = "vwa-theme-appearance";
6 |
7 | watch(isDarkTheme, () => {
8 | if (isDarkTheme.value) {
9 | document.documentElement.classList.add("dark");
10 | localStorage.setItem(APPEARANCE_KEY, "dark");
11 | } else {
12 | document.documentElement.classList.remove("dark");
13 | localStorage.setItem(APPEARANCE_KEY, "");
14 | }
15 | });
16 |
17 | export function initAppearance() {
18 | // document.documentElement.classList.add("dark");
19 | const preference = localStorage.getItem(APPEARANCE_KEY) || "";
20 | const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
21 | if (!preference || preference === "auto" ? prefersDark : preference === "dark") {
22 | document.documentElement.classList.add("dark");
23 | };
24 | }
25 |
26 | export function useAppConfig() {
27 | function closeDrawer() {
28 | isDrawerOpen.value = false;
29 | }
30 |
31 | return {
32 | isDrawerOpen,
33 | isDarkTheme,
34 | closeDrawer,
35 | initAppearance,
36 | };
37 | }
38 |
--------------------------------------------------------------------------------
/src/composables/useI18nLight.ts:
--------------------------------------------------------------------------------
1 | import { type Ref, ref } from "vue";
2 | import enLocale from "@/utils/locales/en.json";
3 | import esLocale from "@/utils/locales/es.json";
4 |
5 | interface Locale {
6 | code: string
7 | name: string
8 | flag?: string
9 | }
10 |
11 | const locales: Locale[] = [
12 | {
13 | code: "en",
14 | name: "English",
15 | },
16 | {
17 | code: "es",
18 | name: "Español",
19 | },
20 | ];
21 |
22 | const locale: Ref = ref(locales[0]);
23 | const messages: Record = {
24 | en: enLocale,
25 | es: esLocale,
26 | };
27 |
28 | export const t = useI18n().t;
29 |
30 | export function useI18n() {
31 | async function initI18n() {
32 | const code
33 | = localStorage.getItem("vue-webapp_lang")
34 | || import.meta.env.VITE_APP_DEFAULT_LOCALE
35 | || "en";
36 | // || window.navigator.language.substring(0, 2);
37 | setLocale(code);
38 | }
39 |
40 | // It can load locales from remote server
41 | async function setLocale(code: string) {
42 | if (locale.value.code !== code) {
43 | locale.value = locales.find(l => l.code === code);
44 | localStorage.setItem("vue-webapp_lang", locale.value.code);
45 | }
46 | }
47 |
48 | function t(msg: string, params: Record = null) {
49 | if (!msg || !locale.value) {
50 | return "";
51 | }
52 |
53 | let val
54 | = msg.split(".").reduce((val, part) => {
55 | return val[part] ?? null;
56 | }, messages[locale.value.code]) ?? msg;
57 | if (params) {
58 | for (const [key, value] of Object.entries(params)) {
59 | val = val.replace(`{${key}}`, value);
60 | }
61 | }
62 | return val ?? msg;
63 | }
64 |
65 | return {
66 | t,
67 | locale,
68 | locales,
69 | setLocale,
70 | initI18n,
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/src/composables/useScreenWidth.ts:
--------------------------------------------------------------------------------
1 | import { ref } from "vue";
2 |
3 | export function useScreenWidth(breakpoints: object) {
4 | const screenWidthFactor = ref("");
5 | const screenSizeMatches = [];
6 |
7 | const breakpointArray = Object.entries(breakpoints).sort((a, b) => +a[1] - +b[1]);
8 |
9 | for (let i = 0; i < breakpointArray.length; i++) {
10 | const clauses = [];
11 | if (i > 0) {
12 | clauses.push(`(min-width: ${breakpointArray[i - 1][1] + 1}px)`);
13 | }
14 |
15 | if (i < breakpointArray.length - 1) {
16 | clauses.push(`(max-width: ${breakpointArray[i][1]}px)`);
17 | }
18 |
19 | const mediaMatch = window.matchMedia(clauses.join(" and "));
20 | mediaMatch.addEventListener("change", (e) => {
21 | if (e.matches) {
22 | screenWidthFactor.value = breakpointArray[i][0];
23 | document.body.classList.add(breakpointArray[i][0]);
24 | } else {
25 | document.body.classList.remove(breakpointArray[i][0]);
26 | }
27 | });
28 | if (mediaMatch.matches) {
29 | screenWidthFactor.value = breakpointArray[i][0];
30 | document.body.classList.add(breakpointArray[i][0]);
31 | } else {
32 | document.body.classList.remove(breakpointArray[i][0]);
33 | }
34 | screenSizeMatches.push(mediaMatch);
35 | }
36 |
37 | return {
38 | screenWidthFactor,
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/src/composables/useSplashScreen.ts:
--------------------------------------------------------------------------------
1 | import { onMounted } from "vue";
2 |
3 | export function useSplashScreen() {
4 | onMounted(() => {
5 | setTimeout(() => {
6 | document.querySelector(".splash-screen")?.classList.add("fade-out");
7 | setTimeout(() => {
8 | document.querySelector("body").classList.remove("splash");
9 | document.body.style.position = "initial";
10 | }, 500);
11 | }, 1000);
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/src/composables/useTouchSwipe.ts:
--------------------------------------------------------------------------------
1 | import type { ComputedRef, Ref } from "vue";
2 | import { computed, reactive, ref } from "vue";
3 |
4 | export type ISwipeDirection = "up" | "down" | "left" | "right" | "none";
5 |
6 | export interface IPoint {
7 | x: number
8 | y: number
9 | }
10 |
11 | export interface ISwipeOptions {
12 | /*
13 | * Specify a custom `window` instance, e.g. working with iframes or in testing environments.
14 | */
15 | window?: Window
16 |
17 | /**
18 | * Register events as passive
19 | *
20 | * @default true
21 | */
22 | passive?: boolean
23 |
24 | /**
25 | * @default 50
26 | */
27 | threshold?: number
28 |
29 | /**
30 | * Callback on swipe start
31 | */
32 | onSwipeStart?: (e: TouchEvent) => void
33 |
34 | /**
35 | * Callback on swipe moves
36 | */
37 | onSwipe?: (e: TouchEvent) => void
38 |
39 | /**
40 | * Callback on swipe ends
41 | */
42 | onSwipeEnd?: (e: TouchEvent, direction: ISwipeDirection) => void
43 | }
44 |
45 | export interface ISwipeReturn {
46 | isSwiping: Ref
47 | direction: ComputedRef
48 | coordsStart: Readonly
49 | coordsEnd: Readonly
50 | stop: () => void
51 | }
52 |
53 | /**
54 | * Reactive touch swipe detection.
55 | *
56 | * @param target
57 | * @param options
58 | */
59 | export function useTouchSwipe(
60 | // target: MaybeRefOrGetter,
61 | target: EventTarget,
62 | options: ISwipeOptions = {},
63 | ): ISwipeReturn {
64 | const { threshold = 50, onSwipe, onSwipeEnd, onSwipeStart } = options;
65 |
66 | const isSwiping = ref(false);
67 | const coordsStart = reactive({ x: 0, y: 0 });
68 | const coordsEnd = reactive({ x: 0, y: 0 });
69 |
70 | const { max, abs, round } = Math;
71 |
72 | const diffX = computed(() => round(coordsStart.x - coordsEnd.x));
73 | const diffY = computed(() => round(coordsStart.y - coordsEnd.y));
74 |
75 | const isThresholdExceeded = computed(() =>
76 | isThresholdExceeded.value || max(abs(diffX.value), abs(diffY.value)) >= threshold);
77 |
78 | const direction = computed((): ISwipeDirection => {
79 | if (!isThresholdExceeded.value) {
80 | return "none";
81 | }
82 |
83 | if (abs(diffX.value) > abs(diffY.value)) {
84 | return diffX.value > 0 ? "left" : "right";
85 | } else { return diffY.value > 0 ? "up" : "down"; }
86 | });
87 |
88 | const listenerOptions: { passive?: boolean; capture?: boolean } = { passive: true, capture: false };
89 |
90 | const onTouchEnd = (e: TouchEvent) => {
91 | if (isSwiping.value) {
92 | onSwipeEnd?.(e, direction.value);
93 | isSwiping.value = false;
94 | }
95 | };
96 |
97 | const onTouchStart = (e: TouchEvent) => {
98 | if (e.touches.length !== 1) {
99 | return;
100 | }
101 |
102 | if (listenerOptions.capture && !listenerOptions.passive) {
103 | e.preventDefault();
104 | }
105 |
106 | coordsStart.x = coordsEnd.x = round(e.touches[0].clientX);
107 | coordsStart.y = coordsEnd.y = round(e.touches[0].clientY);
108 | onSwipeStart?.(e);
109 | };
110 |
111 | const onTouchMove = (e: TouchEvent) => {
112 | if (e.touches.length !== 1) {
113 | return;
114 | }
115 |
116 | coordsEnd.x = round(e.touches[0].clientX);
117 | coordsEnd.y = round(e.touches[0].clientY);
118 | if (!isSwiping.value && isThresholdExceeded.value) {
119 | isSwiping.value = true;
120 | }
121 |
122 | if (isSwiping.value) {
123 | onSwipe?.(e);
124 | }
125 | };
126 |
127 | // useEventListener(target, ["touchend", "touchcancel"], onTouchEnd, listenerOptions),
128 | target.addEventListener("touchstart", onTouchStart, listenerOptions);
129 | target.addEventListener("touchmove", onTouchMove, listenerOptions);
130 | target.addEventListener("touchend", onTouchEnd, listenerOptions);
131 | target.addEventListener("touchcancel", onTouchEnd, listenerOptions);
132 |
133 | function stop() {
134 | target.removeEventListener("touchstart", onTouchStart, listenerOptions);
135 | target.removeEventListener("touchmove", onTouchMove, listenerOptions);
136 | target.removeEventListener("touchend", onTouchEnd, listenerOptions);
137 | target.removeEventListener("touchcancel", onTouchEnd, listenerOptions);
138 | };
139 |
140 | return {
141 | isSwiping,
142 | direction,
143 | coordsStart,
144 | coordsEnd,
145 | stop,
146 | };
147 | }
148 |
--------------------------------------------------------------------------------
/src/layouts/MainLayout.vue:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
62 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 |
3 | // import "./assets/styles/vars.css";
4 | // import "./styles/base.css";
5 |
6 | import "./assets/styles/base.css";
7 | import "./assets/styles/custom.css";
8 | import App from "./App.vue";
9 | import { router } from "./router";
10 | import { loadIcons } from "@/utils/icons";
11 | import { initAppearance } from "@/composables/useAppConfig";
12 | import { api } from "@/services/api";
13 |
14 | // i18n placeholder 1
15 |
16 | const app = createApp(App);
17 | app.use(router);
18 | loadIcons();
19 | initAppearance();
20 | api.init();
21 | // i18n placeholder 2
22 |
23 | await Promise.all([router.isReady()]);
24 |
25 | app.mount("#app");
26 |
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHashHistory } from "vue-router";
2 |
3 | import { routes } from "./routes";
4 |
5 | const router = createRouter({
6 | history: createWebHashHistory(),
7 | routes,
8 | });
9 |
10 | export { router };
11 |
--------------------------------------------------------------------------------
/src/router/routes.ts:
--------------------------------------------------------------------------------
1 | import type { RouteRecordRaw } from "vue-router";
2 | import HomeView from "@/views/HomeView.vue";
3 | import AboutView from "@/views/AboutView.vue";
4 | import ContactsView from "@/views/ContactsView.vue";
5 |
6 | const routes: RouteRecordRaw[] = [
7 | {
8 | path: "/",
9 | name: "home",
10 | component: HomeView,
11 | },
12 | {
13 | path: "/contacts",
14 | name: "contacts",
15 | component: ContactsView,
16 | },
17 | {
18 | path: "/about",
19 | name: "about",
20 | component: AboutView,
21 | },
22 | ];
23 |
24 | export { routes };
25 |
--------------------------------------------------------------------------------
/src/services/api/http.ts:
--------------------------------------------------------------------------------
1 | // import axios from "axios";
2 | // import type { AxiosRequestConfig } from "axios";
3 | // import HttpRequest from "./xhr";
4 |
5 | interface IOptions {
6 | baseUrl: string
7 | headers?: Record
8 | token?: Function
9 | logout?: Function
10 | }
11 |
12 | let options: IOptions = null;
13 |
14 | const http = {
15 |
16 | setOptions(_options) {
17 | options = _options;
18 | },
19 |
20 | async post(data: any, uri: string) {
21 | if (options.token()) {
22 | options.headers.Authorization = `Bearer ${options.token()}`;
23 | }
24 | return postFetch(data, uri);
25 | // return postXhr(data, uri);
26 | // return postAxios(data, uri);
27 | },
28 |
29 | async get(url) {
30 | try {
31 | return fetch(url)
32 | .then(response => response.json());
33 | } catch (error) {
34 | console.log(error);
35 | }
36 | },
37 |
38 | };
39 |
40 | async function postFetch(data: any, uri: string) {
41 | const url = uri.startsWith("http") ? uri : `${options.baseUrl}${uri}`;
42 | return fetch(url, {
43 | method: "POST",
44 | headers: options.headers,
45 | // credentials: "include",
46 | body: JSON.stringify(data),
47 | })
48 | .then(response => response.json());
49 | }
50 |
51 | // Uncomment, if you are using `XMLHttpRequest`
52 |
53 | // async function postXhr(data: any, uri: string) {
54 | // const xhr = new HttpRequest("POST", `${options.baseUrl}${uri}`, "application/json", options.headers);
55 | // xhr.xhr.withCredentials = true;
56 | // const response = await xhr.send(data);
57 | // return response.json;
58 | // }
59 |
60 | // Uncomment, if you are using `axios`
61 |
62 | // async postAxios(data: any, uri: String) {
63 | // const config: AxiosRequestConfig = {
64 | // method: "POST",
65 | // url: `${options.baseUrl}${uri}`,
66 | // data,
67 | // withCredentials: true,
68 | // headers: options.headers,
69 | // };
70 | // try {
71 | // return await axios.request(config);
72 | // } catch (error) {
73 | // console.log(error);
74 | // }
75 | // },
76 |
77 | export default http;
78 |
--------------------------------------------------------------------------------
/src/services/api/index.ts:
--------------------------------------------------------------------------------
1 | // import auth from "./auth";
2 | import utils from "./utils";
3 | import { authInterceptor, notificationInterceptor } from "./interceptors";
4 | import jsonrpc from "./jsonrpc";
5 | import http from "./http";
6 |
7 | // import { useAuth } from "@/user-account/composables/useAuth";
8 |
9 | // const auth: any = null;
10 |
11 | const api = {
12 | utils,
13 | http,
14 | init() {
15 | http.setOptions({
16 | baseUrl: import.meta.env.VITE_API_URL,
17 | headers: { "Content-Type": "application/json" },
18 | token: () => null,
19 | logout: () => null,
20 | });
21 | jsonrpc.addResponseInterceptor(notificationInterceptor);
22 | // jsonrpc.addResponseInterceptor(authInterceptor);
23 | },
24 | };
25 |
26 | export { api };
27 | export default api;
28 |
--------------------------------------------------------------------------------
/src/services/api/interceptors.ts:
--------------------------------------------------------------------------------
1 | import type { JsonRpcResponseMessage } from "./jsonrpc";
2 |
3 | // import { t } from "@/app/composables/useI18n";
4 | // import { toast } from "@/app/utils/notification";
5 | // import { useAuth } from "@/user-account/composables/useAuth.js";
6 | // import { router } from "@/app/router";
7 |
8 | export const notificationInterceptor = {
9 | process(data: JsonRpcResponseMessage) {
10 | // const message = data.error?.message || data.result?.message;
11 | if (data.result?.message) {
12 | // const msg = data.result.data?.i18n ? t(`messages.${data.result.data?.i18n}`) : data.result.message;
13 | const msg = data.result.message;
14 | // toast.info(msg);
15 | console.log("JSON-RPC message: ", msg);
16 | }
17 | if (data.error?.message) {
18 | // const msg = data.error.data?.i18n ? t(`errors.${data.error.data?.i18n}`) : data.error.message;
19 | const msg = data.error.message;
20 | // toast.warn(msg);
21 | console.log("JSON-RPC error: ", msg, data.error.data);
22 | }
23 | },
24 | };
25 |
26 | export const authInterceptor = {
27 | /**
28 | * Logout user if response returned error code 401
29 | * We use it in JSON-RPC instead of HTTP 401 response code
30 | */
31 | process(data: JsonRpcResponseMessage) {
32 | if (data.error?.code === 401) {
33 | // useAuth().logout();
34 | }
35 | },
36 | };
37 |
--------------------------------------------------------------------------------
/src/services/api/jsonrpc.interfaces.ts:
--------------------------------------------------------------------------------
1 | interface JsonRpcMessage {
2 | jsonrpc: string
3 | }
4 | interface JsonRpcRequestMessage extends JsonRpcMessage {
5 | id?: number | string
6 | method: string
7 | // params?: object
8 | params?: Record
9 | }
10 | interface JsonRpcResponseMessage extends JsonRpcMessage {
11 | /**
12 | * The request id.
13 | */
14 | id: number | string | null
15 |
16 | /**
17 | * The result of a request. This member is REQUIRED on success.
18 | * This member MUST NOT exist if there was an error invoking the method.
19 | */
20 | result?: any
21 | // result?: string | number | boolean | object | null | any
22 |
23 | /**
24 | * The error object in case a request fails.
25 | */
26 | error?: ResponseError
27 | }
28 | interface ResponseError {
29 | /**
30 | * A number indicating the error type that occurred.
31 | */
32 | code: number
33 |
34 | /**
35 | * A string providing a short description of the error.
36 | */
37 | message: string
38 |
39 | /**
40 | * A primitive or structured value that contains additional
41 | * information about the error. Can be omitted.
42 | */
43 | data?: string | number | boolean | Array | object | null | any
44 | }
45 |
46 | interface JsonRpcPayload {
47 | id?: string | number
48 | method: string
49 | // params?: { origin: string };
50 | params?: JsonRpcPayloadParams
51 | }
52 |
53 | interface JsonRpcPayloadParams {
54 | [key: string]: string | number | boolean | object
55 | }
56 |
57 | interface JsonRpcPayloadOptions {
58 | isNotification?: boolean
59 | uri?: string
60 | token?: string
61 | fullResponse?: boolean
62 | }
63 |
64 | export type {
65 | JsonRpcMessage,
66 | JsonRpcRequestMessage,
67 | JsonRpcResponseMessage,
68 | ResponseError,
69 | JsonRpcPayload,
70 | JsonRpcPayloadParams,
71 | JsonRpcPayloadOptions,
72 | };
73 |
74 | // export ErrorCodes {
75 | // // Defined by JSON-RPC
76 | // export const ParseError: integer = -32700;
77 | // export const InvalidRequest: integer = -32600;
78 | // export const MethodNotFound: integer = -32601;
79 | // export const InvalidParams: integer = -32602;
80 | // export const InternalError: integer = -32603;
81 |
82 | // /**
83 | // * This is the start range of JSON-RPC reserved error codes.
84 | // * It doesn't denote a real error code. No LSP error codes should
85 | // * be defined between the start and end range. For backwards
86 | // * compatibility the `ServerNotInitialized` and the `UnknownErrorCode`
87 | // * are left in the range.
88 | // *
89 | // * @since 3.16.0
90 | // */
91 | // export const jsonrpcReservedErrorRangeStart: integer = -32099;
92 | // /** @deprecated use jsonrpcReservedErrorRangeStart */
93 | // export const serverErrorStart: integer = jsonrpcReservedErrorRangeStart;
94 |
95 | // /**
96 | // * Error code indicating that a server received a notification or
97 | // * request before the server has received the `initialize` request.
98 | // */
99 | // export const ServerNotInitialized: integer = -32002;
100 | // export const UnknownErrorCode: integer = -32001;
101 |
102 | // /**
103 | // * This is the end range of JSON-RPC reserved error codes.
104 | // * It doesn't denote a real error code.
105 | // *
106 | // * @since 3.16.0
107 | // */
108 | // export const jsonrpcReservedErrorRangeEnd = -32000;
109 | // /** @deprecated use jsonrpcReservedErrorRangeEnd */
110 | // export const serverErrorEnd: integer = jsonrpcReservedErrorRangeEnd;
111 |
112 | // /**
113 | // * This is the start range of LSP reserved error codes.
114 | // * It doesn't denote a real error code.
115 | // *
116 | // * @since 3.16.0
117 | // */
118 | // export const lspReservedErrorRangeStart: integer = -32899;
119 |
120 | // /**
121 | // * A request failed but it was syntactically correct, e.g the
122 | // * method name was known and the parameters were valid. The error
123 | // * message should contain human readable information about why
124 | // * the request failed.
125 | // *
126 | // * @since 3.17.0
127 | // */
128 | // export const RequestFailed: integer = -32803;
129 |
130 | // /**
131 | // * The server cancelled the request. This error code should
132 | // * only be used for requests that explicitly support being
133 | // * server cancellable.
134 | // *
135 | // * @since 3.17.0
136 | // */
137 | // export const ServerCancelled: integer = -32802;
138 |
139 | // /**
140 | // * The server detected that the content of a document got
141 | // * modified outside normal conditions. A server should
142 | // * NOT send this error code if it detects a content change
143 | // * in it unprocessed messages. The result even computed
144 | // * on an older state might still be useful for the client.
145 | // *
146 | // * If a client decides that a result is not of any use anymore
147 | // * the client should cancel the request.
148 | // */
149 | // export const ContentModified: integer = -32801;
150 |
151 | // /**
152 | // * The client has canceled a request and a server as detected
153 | // * the cancel.
154 | // */
155 | // export const RequestCancelled: integer = -32800;
156 |
157 | // /**
158 | // * This is the end range of LSP reserved error codes.
159 | // * It doesn't denote a real error code.
160 | // *
161 | // * @since 3.16.0
162 | // */
163 | // export const lspReservedErrorRangeEnd: integer = -32800;
164 | // }
165 |
--------------------------------------------------------------------------------
/src/services/api/jsonrpc.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | JsonRpcPayload,
3 | JsonRpcPayloadOptions,
4 | JsonRpcPayloadParams,
5 | JsonRpcRequestMessage,
6 | JsonRpcResponseMessage,
7 | } from "./jsonrpc.interfaces";
8 | import http from "./http";
9 |
10 | let jsonCounter = 0;
11 | let logout;
12 | let metaDataCallback = () => {};
13 |
14 | const responseInterceptors = [];
15 |
16 | const jsonrpc = {
17 |
18 | addResponseInterceptor(interceptor) {
19 | responseInterceptors.push(interceptor);
20 | },
21 | setLogoutCallback(_logout) {
22 | logout = _logout;
23 | },
24 | setMetaDataCallback(_metaDataCallback) {
25 | metaDataCallback = _metaDataCallback;
26 | },
27 |
28 | };
29 |
30 | /**
31 | * Executes a JSON-RPC request and returns the response data.
32 | *
33 | * @param {JsonRpcPayload | Array} payload - The JSON-RPC payload(s) to execute.
34 | * @param {JsonRpcPayloadOptions} [options] - Options for the JSON-RPC request.
35 | * @return {Promise} A promise that resolves with the response data.
36 | */
37 | async function jsonRpc(
38 | payload: JsonRpcPayload | Array,
39 | options?: JsonRpcPayloadOptions,
40 | ) {
41 | let data: JsonRpcRequestMessage | Array;
42 | // const auth = useAuth();
43 | try {
44 | if (Array.isArray(payload)) {
45 | data = payload.map((message) => {
46 | return buildRequestMessage(message, options);
47 | });
48 | } else {
49 | data = buildRequestMessage(payload, options);
50 | }
51 |
52 | const response = await http.post(data, buildUri(payload, options));
53 | // const response = await axios.request(config);
54 | if (options?.fullResponse) {
55 | responseInterceptors.forEach((interceptor) => {
56 | interceptor.process(response);
57 | });
58 | return response;
59 | }
60 |
61 | if (Array.isArray(response)) {
62 | response.forEach((msg: JsonRpcResponseMessage) => {
63 | responseInterceptors.forEach((interceptor) => {
64 | interceptor.process(msg);
65 | });
66 | });
67 | return response;
68 | } else {
69 | responseInterceptors.forEach((interceptor) => {
70 | interceptor.process(response);
71 | });
72 | if (response.result) {
73 | return response.result;
74 | }
75 | }
76 | } catch (error) {
77 | console.log(error);
78 | if (error.response?.status === 401) {
79 | logout();
80 | }
81 | }
82 | }
83 | /**
84 | * Builds a JSON-RPC request message from the given payload and options.
85 | *
86 | */
87 | function buildRequestMessage(
88 | payload: JsonRpcPayload,
89 | options?: JsonRpcPayloadOptions,
90 | ): JsonRpcRequestMessage {
91 | const message: JsonRpcRequestMessage = {
92 | jsonrpc: "2.0",
93 | method: payload.method,
94 | params: payload.params || {},
95 | };
96 |
97 | if (!options?.isNotification) {
98 | message.id = payload.id ?? jsonCounter++;
99 | }
100 | message.params.meta = metaDataCallback();
101 | // message.params.meta = {
102 | // ...metaData,
103 | // at: getToken(),
104 | // };
105 | // Object.assign(message.params, { meta: {...metaData, { token: getToken() }});
106 | return message;
107 | }
108 |
109 | /**
110 | * Returns the URI based on the given payload and options.
111 | *
112 | * @param {JsonRpcPayload | Array} payload - The payload or array of payloads.
113 | * @param {JsonRpcPayloadOptions} [options] - The options that may contain the URI.
114 | * @return {string} The URI for the given payload and options.
115 | */
116 | function buildUri(
117 | payload: JsonRpcPayload | Array,
118 | options?: JsonRpcPayloadOptions,
119 | ) {
120 | if (options?.uri) {
121 | return options?.uri;
122 | }
123 | if (Array.isArray(payload)) {
124 | return (
125 | `batch[${payload.map((p: JsonRpcPayload) => p.method).join("+")}]`
126 | );
127 | } else {
128 | return payload.method.replace(".", "/");
129 | }
130 | }
131 |
132 | // export function _jsonRpc(
133 | // payload: JsonRpcPayload | Array,
134 | // options?: JsonRpcPayloadOptions,
135 | // ) {
136 | // return jsonrpc.jsonRpc(payload, options);
137 | // }
138 |
139 | export {
140 | type JsonRpcPayloadOptions,
141 | type JsonRpcPayload,
142 | type JsonRpcPayloadParams,
143 | type JsonRpcRequestMessage,
144 | type JsonRpcResponseMessage,
145 | jsonRpc,
146 | };
147 |
148 | export default jsonrpc;
149 |
--------------------------------------------------------------------------------
/src/services/api/notes.txt:
--------------------------------------------------------------------------------
1 | import { jsonRpc } from "#/services/json-rpc/jsonrpc";
2 |
3 | async providers() {
4 | const providers = await jsonRpc(
5 | {
6 | method: "utils.resources:list",
7 | params: {
8 | database: "slotegrator",
9 | resource: "provider",
10 | // where: "id < 3"
11 | },
12 | },
13 | { uri: "casino.providers:list" },
14 | );
15 | return providers.map((provider) => {
16 | return {
17 | ...provider,
18 | slug: provider.name.toLowerCase().replaceAll(" ", "-"),
19 | };
20 | });
21 | },
22 |
23 | async updateFavourite(params) {
24 | return await jsonRpc({
25 | method: "casino.favourites:update",
26 | params,
27 | });
28 | },
29 |
--------------------------------------------------------------------------------
/src/services/api/utils.ts:
--------------------------------------------------------------------------------
1 | import http from "./http";
2 | import { jsonRpc } from "./jsonrpc";
3 |
4 | const utils = {
5 |
6 | async testRest() {
7 | return http.get("https://jsonplaceholder.typicode.com/todos/1");
8 | },
9 |
10 | async testJsonRpc() {
11 | return jsonRpc({
12 | method: "getBestBlockHash",
13 | params: {},
14 | }, { uri: "https://seed-1.testnet.networks.dash.org:1443/" });
15 | },
16 |
17 | };
18 |
19 | export default utils;
20 |
--------------------------------------------------------------------------------
/src/services/api/xhr.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | class HttpRequest {
4 | constructor(method, uri, contentType, headers) {
5 | this._fetchingPromise = null;
6 | this.xhr = new XMLHttpRequest();
7 | if (!!method && !!uri) {
8 | this.xhr.open(method, uri);
9 | }
10 | if (contentType) {
11 | this.xhr.setRequestHeader("Content-Type", contentType);
12 | }
13 | // if (headers && headers.length) {
14 | if (headers) {
15 | Object.keys(headers).forEach((key) => {
16 | this.xhr.setRequestHeader(key, headers[key]);
17 | });
18 | // headers.forEach((h) => {
19 | // this.xhr.setRequestHeader(h.header, h.value);
20 | // });
21 | }
22 | }
23 |
24 | open(method, uri) {
25 | if (!!method && !!uri) {
26 | this.xhr.open(method, uri);
27 | }
28 | }
29 |
30 | setRequestHeader(header, value) {
31 | if (!!header && !!value) {
32 | this.xhr.setRequestHeader(header, value);
33 | }
34 | }
35 |
36 | getAllResponseHeaders() {
37 | return this.headers;
38 | }
39 |
40 | fetchFromServer(payload) {
41 | let ret = null;
42 |
43 | ret = new Promise((resolve, reject) => {
44 | this.xhr.onload = function () {
45 | if (this.readyState === this.HEADERS_RECEIVED) {
46 | this.headers = this.xhr.getAllResponseHeaders();
47 | }
48 |
49 | if (this.xhr.status === 200) {
50 | const d = JSON.parse(this.xhr.responseText, (k, v) => {
51 | if (!!v && ((k === "createdon" || k === "updatedon" || k === "askedon"
52 | || k === "publishedon" || k === "lastUpdated") && Date.parse(v))) {
53 | return new Date(v);
54 | }
55 | if (!!v && (k === "json") && typeof v == "string") {
56 | return JSON.parse(v);
57 | }
58 | return v;
59 | });
60 | resolve(
61 | {
62 | json: d,
63 | headers: this.headers,
64 | },
65 | );
66 | } else {
67 | reject(this.xhr.statusText || (`status ${this.xhr.status}`));
68 | }
69 | this._fetchingPromise = null;
70 | }.bind(this);
71 |
72 | this.xhr.onerror = (e) => {
73 | reject(e.target.status);
74 | this._fetchingPromise = null;
75 | };
76 |
77 | if (payload instanceof FormData) {
78 | this.xhr.send(payload);
79 | } else {
80 | this.xhr.send(JSON.stringify(payload));
81 | }
82 |
83 | // this.xhr.onreadystatechange = function () {
84 | // if (this.readyState == this.HEADERS_RECEIVED) {
85 | // console.log("headers", request.getAllResponseHeaders());
86 | // }
87 | // }
88 | });
89 |
90 | return ret;
91 | }
92 |
93 | send(payload) {
94 | if (!this._fetchingPromise) {
95 | this._fetchingPromise = this.fetchFromServer(payload);
96 | }
97 |
98 | return this._fetchingPromise;
99 | }
100 | }
101 |
102 | export default HttpRequest;
103 |
--------------------------------------------------------------------------------
/src/utils/icons.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | const svgResources = new Map();
4 | const imageResources = new Map();
5 |
6 | function loadIcons() {
7 | let modules = import.meta.glob("@/assets/images/**/*.svg", {
8 | query: "?raw",
9 | import: "default",
10 | eager: true,
11 | });
12 | for (const fileName in modules) {
13 | const name = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length - 4);
14 | svgResources.set(name, modules[fileName]);
15 | }
16 |
17 | modules = import.meta.glob("@/assets/images/**/*.png", {
18 | query: "?url",
19 | import: "default",
20 | eager: true,
21 | });
22 | // debugger;
23 | for (const fileName in modules) {
24 | const name = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length - 4);
25 | imageResources.set(name, modules[fileName]);
26 | }
27 | // console.log(imageResources);
28 | // console.log(getIcon("lobby"));
29 | }
30 |
31 | function getSvgIcon(name) {
32 | return svgResources.get(name);
33 | }
34 |
35 | function getImageUrl(name) {
36 | return imageResources.get(name);
37 | }
38 |
39 | // export svgResources;
40 |
41 | // export default iconMap;
42 | // export { iconMap, getIcon, svgResources };
43 | export { loadIcons, getSvgIcon, getImageUrl, svgResources };
44 |
--------------------------------------------------------------------------------
/src/utils/injections/gtag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/src/utils/injections/injection-config.ts:
--------------------------------------------------------------------------------
1 | import type { IHtmlInjectionConfig } from "vite-plugin-html-injection";
2 |
3 | export const htmlInjectionConfig: IHtmlInjectionConfig = {
4 | injections: [
5 | {
6 | name: "Open Graph",
7 | path: "./src/utils/injections/open-graph.html",
8 | type: "raw",
9 | injectTo: "head",
10 | },
11 | // {
12 | // name: "Version checker",
13 | // path: "./src/utils/injections/version-checker.js",
14 | // type: "js",
15 | // injectTo: "head-prepend",
16 | // },
17 | {
18 | name: "Splash screen",
19 | path: "./src/utils/injections/splash-screen.html",
20 | type: "raw",
21 | injectTo: "body-prepend",
22 | },
23 | {
24 | name: "Service worker",
25 | path: "./src/utils/injections/sw.js",
26 | type: "js",
27 | injectTo: "head",
28 | },
29 | {
30 | name: "Google analytics",
31 | path: "./src/utils/injections/gtag.html",
32 | type: "raw",
33 | injectTo: "body",
34 | },
35 | ],
36 | };
37 |
--------------------------------------------------------------------------------
/src/utils/injections/open-graph.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/utils/injections/splash-screen.html:
--------------------------------------------------------------------------------
1 |
152 |
153 |
154 |
155 |
156 |
161 |
162 |
project-name
163 |
164 |
--------------------------------------------------------------------------------
/src/utils/injections/sw.js:
--------------------------------------------------------------------------------
1 | if ("serviceWorker" in navigator) {
2 | navigator.serviceWorker.register("/service-worker.js");
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "msg": "Hello"
3 | }
4 |
--------------------------------------------------------------------------------
/src/utils/locales/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "msg": "Hola"
3 | }
4 |
--------------------------------------------------------------------------------
/src/views/AboutView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
About this tool
6 |
7 | The goal of the `create-vue-webapp` tool is to allow users to scaffold a new customizable vue web
8 | application.
9 |
10 |
11 |
12 | Create a simple working SPA front-end application for a specific task in a few minutes, style it and
13 | fill it with content in a few hours.
14 |
15 | Customize it with small functional code blocks.
16 |
17 | Collect and summarize the best techniques and practices for building a modern reactive frontend.
18 |
19 |
20 | Enable novice developers to learn frontend and Vue 3 best practices from building application
21 | architecture to naming CSS classes with ready-made examples.
22 |
23 |
24 | Enable experienced developers to quickly and more efficiently reuse overused boilerplate code,
25 | especially that which cannot be distributed as NPM packages.
26 |
27 |
28 | This application should act as a website builder, with the ability to choose when creating:
29 |
30 | A global business website template (portfolio, blog, online store, etc.)
31 | A variant of the general layout of the web application
32 | Variants of specific components (header, footer, sidebar, etc.)
33 | General style/design options
34 |
35 | Separate functionality injected as functional fragments (API module, i18n, PWA, splash screen,
36 | auth module, themes, etc.)
37 |
38 | Ability to connect/integrate common libraries (including UI) if desired
39 | Possibility to integrate with certain headless CRM and other backend API services.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/views/ContactsView.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
56 |
57 |
58 |
59 |
111 |
--------------------------------------------------------------------------------
/src/views/HomeView.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
Congratulations with scaffolding your vue webapp!
19 |
20 | Selected options:
21 |
22 |
23 |
28 | {{ option.name }}
29 | {{ option.value }}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
59 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "lib": ["ES2021", "DOM", "ScriptHost"],
5 | "baseUrl": ".",
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "paths": { "@/*": ["./src/*"] },
9 | "resolveJsonModule": true,
10 | "typeRoots": ["./types"],
11 | "allowJs": true,
12 | "outDir": "./dist",
13 | "esModuleInterop": true
14 | // "types": ["node", "vite/client"]
15 | },
16 | "exclude": [
17 | "./dist"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from "node:path";
2 | import { defineConfig } from "vite";
3 | import vue from "@vitejs/plugin-vue";
4 | import { htmlInjectionPlugin } from "vite-plugin-html-injection";
5 | import { htmlInjectionConfig } from "./src/utils/injections/injection-config";
6 |
7 | // import { URL, fileURLToPath } from "node:url";
8 | // https://vitejs.dev/config/
9 | export default defineConfig({
10 | base: "/vue-webapp/",
11 | build: {
12 | target: "esnext",
13 | },
14 | plugins: [
15 | vue(),
16 | htmlInjectionPlugin(htmlInjectionConfig),
17 | ],
18 | resolve: {
19 | alias: {
20 | "@": path.resolve(__dirname, "./src"),
21 | },
22 | },
23 | });
24 |
--------------------------------------------------------------------------------