Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tag component #507

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d536451
Add Deno and Node.js badge
Sep 18, 2022
14299f5
Merge branch 'main' into badge
Sep 19, 2022
97d4291
Apply suggestions from code review
Sep 19, 2022
da70b26
Redesign the badge
Sep 19, 2022
f06b4f7
Implement badge into plugin
Sep 19, 2022
1db7dd7
Add Tag component
Sep 20, 2022
94c8558
Update all tags
Sep 20, 2022
3f701c5
Merge branch 'main' into badge
Sep 20, 2022
c6df756
Update tag
Sep 24, 2022
9647995
Fix typo on menu entry
Sep 24, 2022
50e1525
Sync updates to all languages
Sep 24, 2022
3b95be2
Fix build error
Sep 24, 2022
92c4c8a
Update tag component
Sep 30, 2022
1d9fc86
Reduce delay and fix icon transition
Oct 1, 2022
0c5d208
Fix alignment on mobile view
Oct 1, 2022
90e0dd8
Change <a> element dynamically to increase SEO score
Oct 1, 2022
d1b77b6
Add favicon to third-party plugin menu
Oct 1, 2022
347b473
Merge branch 'main' into badge
KnorpelSenf Oct 2, 2022
190a5b3
Adjust TagGroup margin
Oct 4, 2022
bf5fb4c
Add Autotag plugin
Oct 5, 2022
1f24a0d
Remove tag from plugins page
Oct 7, 2022
0c5add6
Merge branch 'main' into badgex
Oct 7, 2022
8a4c853
Fix css
Oct 7, 2022
9c16c6f
Rename component to circumvent adblocker
Oct 7, 2022
c83f639
Replace tags with autotag
Oct 7, 2022
6c78490
Merge branch 'main' into badge
Oct 7, 2022
7dbef4a
Merge branch 'badgex' into badge
Oct 7, 2022
ee29599
Update extensions list
Oct 7, 2022
fad5c9a
Add dynamic link referencing to asset files
Oct 8, 2022
4d4cbab
Rename icon files
Oct 8, 2022
808738f
Rename `type` property to `template`
Oct 8, 2022
8253351
Add some workspace tools and configs
Oct 8, 2022
509bbbe
Refactor
Oct 8, 2022
07df0e1
update tsconfig
Oct 8, 2022
41bb81c
Add autotagMenu plugin
Oct 9, 2022
c07fd0a
Merge branch 'main' into badge-autotagmenu
Oct 13, 2022
df5b131
Add transition
Oct 15, 2022
f4ff085
Add `gap` option to `TagGroup`
Oct 15, 2022
04f5f89
Merge branch 'main' into badge
Oct 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"printWidth": 100
}
4 changes: 3 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"esbenp.prettier-vscode",
"davidanson.vscode-markdownlint",
"bierner.markdown-checkbox",
"denoland.vscode-deno"
"denoland.vscode-deno",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin"
]
}
11 changes: 9 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@
"./site/docs/hosting/",
"./site/docs/plugins/",
"./site/docs/resources/",
"./site/docs/zh/"
]
"./site/docs/zh/",
"./site/docs/es/",
"./site/docs/id/"
],
"editor.formatOnType": true,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "modifications",
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
1 change: 1 addition & 0 deletions site/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ yarn.lock

# dist directory
dist/
.prettierrc
142 changes: 142 additions & 0 deletions site/docs/.vuepress/components/AutotagMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<script lang="ts">
declare const __AUTOTAGMENU_CONFIG__: AutotagMenuOptions; // From .vuepress/plugins/autotagMenu.ts
declare const __NAVBAR_CONFIG__: false | NavbarConfig | undefined; // From .vuepress/theme/grammyTheme.ts
</script>

<script setup lang="ts">
import { useRoute } from "vue-router";
import type {
AutotagMenuOptions,
AutotagMenuTag,
NavbarConfig,
NavbarGroup,
NavLink,
} from "../types/index";

const props = defineProps({
// From `NavbarItems.vue`, `NavbarDropdown.vue`, and `AutoLink.vue`
currentMenu: { type: Array<string>, required: true },
menuIndex: { type: Array<number>, required: true },
});

const menuConfig = __AUTOTAGMENU_CONFIG__; // autotagMenu plugin config
const navbarConfig = __NAVBAR_CONFIG__; // Array of all navbar menus from .vuepress/config.ts
const tagList: AutotagMenuTag[] = []; // Collection of tags to be shown
const route = useRoute().path; // Current URL path
let showTag = false; // Should render tag? (Do not render if nothing to show).

let currentMenu: string[] = [];
let _tmp: (string | NavLink | NavbarGroup)[] = [];

if (navbarConfig) {
/**
* props.menuIndex: number[]
*
* Current menu position in navbar (menu index starts from 0).
* We use index to locate the exact position of a menu since
* the name of the menu will change depending on the selected language.
*
* @example
* Navbar menu in grammy.dev
* .
* ├── Guide/
* ├── Learn/
* │ └── Guide
* │ │ └── Overview
* │ │ └── Introduction
* │ │ └── Getting Started (*)
* │ │ └── ...
* │ └── Advanced
* │ └── Overview (^)
* | └── ...
* ├── Plugins/
* │ └── Introduction
* | ...
* ├── Examples
* ...
*
* ex:
* (*) [1, 0, 2] means we are currently on 2nd menu (Learn), 1st submenu (Guide), 3rd item (Getting Started)
* (^) [1, 1, 0] means we are currently on 2nd menu (Learn), 2nd submenu (Advanced), 1st item (Overview)
* */

// Get the menu index and convert them to the actual menu name.
for (const i of props.menuIndex) {
// Get the navbar menu name based on current index
let item = _tmp.length ? _tmp[i] : navbarConfig[i];

if (typeof item !== "string" && item) {
currentMenu.push(item.text); // Store the name

if ("children" in item) {
// If it contains a sub-menu, store them in a temporary variable for later processing.
_tmp = item.children;
}
} else {
/*
Index is out of range. Exit the loop.
This happens when the index contains menus that are generated automatically by vuepress.
For example: language menu, Github repo link, search menu, etc.
*/
break;
}
}
}

// Now match the current menu with the plugin config.
menuConfig.forEach((option) => {
// Extract the paths
for (const path of option.path) {
let pathFound = false; // is the path matched with the current menu?
let excludeFound = false; // Found the excluded menu item?
let includeFound = true; // Found the included menu item?
const exclude = option.exclude?.join("|"); // Regex. Match at least one of these items
const include = option.include?.join("|"); // Regex. Match at least one of these items
const currentMenuPath = currentMenu?.join(";"); // Regex. Separate each path with ;
const pathString = path.join(";"); // // Regex. Separate each path with ;
const regex = new RegExp(`^${pathString}`); // Prepare regex. Is this path matched with current menu?
const regexExclude = new RegExp(`^${pathString}.*;(${exclude})`); // Prepare regex. Is there any excluded item in this path.
const regexInclude = new RegExp(`^${pathString}.*;(${include})`); // Prepare regex. Is there any included item in this path.

// Filter the sub-menu but do not include the menu itself (parent menu).
if (currentMenuPath && pathString !== currentMenuPath) {
// Begins the matching process.
pathFound = regex.test(currentMenuPath);
if (exclude) {
excludeFound = regexExclude.test(currentMenuPath);
}
if (include) {
includeFound = regexInclude.test(currentMenuPath);
}
}

// If the path is matched with the current menu
// AND the included menu item is found
// AND does not contain any excluded menu item.
if (pathFound && includeFound && !excludeFound) {
// Extract the tags
option.tag.forEach((tag) => {
const _tag = { ...tag }; // Copy tag to temporary variable to protect the original one from being changed.
_tag.template ??= "iconOnly"; // If the tag template is empty, assign with value "iconOnly" (default template)
_tag.desc = tag.desc;

// If current page is in locale, change the tag description according to the selected language.
for (const lang in tag.locale) {
if (route.startsWith(`/${lang}/`)) {
_tag.desc = tag.locale[lang];
break;
}
}
tagList.push(_tag); // Add tag to the list
});
showTag = true; // The criteria have been met. Render the tag!
}
}
});
</script>

<template>
<TagGroup v-if="showTag">
<Tag v-for="item in tagList" :autotag="item" />
</TagGroup>
</template>
86 changes: 86 additions & 0 deletions site/docs/.vuepress/components/AutotagPage.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<script setup lang="ts">
import { useRoute } from "vue-router";
import TagGroup from "./TagGroup.vue";
import Tag from "./Tag.vue";
import type { AutotagUrl, AutotagOptions, Props } from "../types";

const props = defineProps({ config: String });
let showTag = false;
let tagList: Props[] = []; // Collection of tags to be shown

if (props.config) {
// Get current URL page then delete trailing "/" and anchor "#"
const route = useRoute().fullPath;
const currentUrl = route.replace(/\/$|#.*$/, "");
const autotag: AutotagOptions = JSON.parse(props.config);

for (const item of autotag) {
const urls = item.url;
const locale = item.tag[0].locale;

// Inject locale url
item.url.forEach((url) => {
for (const lang in locale) {
const localeUrl: AutotagUrl = `/${lang}${url}`; // ex: "/plugins" => "/id/plugins"
urls.push(localeUrl);
}
});

for (let url of urls) {
// Reset the variables
let include = true;
let exclude = true;

const _include = item.include ?? [""]; // [""] will always evaluate to true on searchUrl function
const _exclude = item.exclude ?? []; // [] will always evaluate to false on searchUrl function

// Check if the current url page is matched with the user options.
include = matchUrl(_include, url, currentUrl, route);
exclude = !matchUrl(_exclude, url, currentUrl, route);

if (include && exclude) {
item.tag.forEach((tag) => {
// Check if locale property exist
for (const lang in tag.locale) {
// Check if current page is a locale page
const isLocalePage = currentUrl.startsWith(`/${lang}/`);
if (isLocalePage) {
tag.text = tag.locale[lang].text ?? tag.text; // Replace tag text with the locale one
tag.desc = tag.locale[lang].desc ?? tag.desc; // Replace tag description with the locale one
break;
}
}
tagList.push(tag); // Add tag to the list
});
showTag = true;
break;
}
}
}
}

function matchUrl(items: string[], url: string, currentUrl: string, route: string): boolean {
let result = false;

for (const item of items) {
// Match user url with current url page
if (item === "/") {
// "/" is special syntax for index html
result = new RegExp(`^${url}\/$`).test(route); // https://grammy.dev/url/
} else {
result = item.startsWith("/")
? new RegExp(`^${url}${item}`).test(currentUrl) // https://grammy.dev/url/item ...
: new RegExp(`^${url}.*${item}`).test(currentUrl); // https://grammy.dev/url ... item ...
}

if (result) break;
}
return result;
}
</script>

<template>
<TagGroup v-if="showTag">
<Tag v-for="item in tagList" :autotag="item" />
</TagGroup>
</template>
Loading