import Script from 'next/script'; import React from 'react'; import { ArticleLink } from '../dto/site/ArticleLink'; import { SiteData } from '../dto/site/SiteData'; import { SiteTab } from '../dto/site/SiteTab'; import { SiteUtils } from './SiteUtils'; // Tab list options type TabListOptions = React.SelectHTMLAttributes & { classNames?: string; emptyOptionLabel?: string; }; /** * Local site class * 本地站点类 */ export class LocalSite { /** * Constructor * 构造函数 * @param data Site data */ constructor(public readonly data: SiteData) {} /** * Clear title format * 清除标题格式 * @param title Title */ clearTitle(title: string | undefined) { if (title == null) return undefined; const reg = /\[\[[^\[\]]+\]\]/g; const parts = title.split(reg); let match: RegExpMatchArray | null; let index = 0; while ((match = reg.exec(title)) !== null) { const part = match[0].slice(2, -2); parts.splice(index * 2 + 1, 0, part); index++; } return parts.join(''); } private createMenuItem(t: SiteTab, hasIcon?: boolean) { return hasIcon ? ( {t.name} ) : ( t.name ); } /** * Create Bootstrap menu * 创建 Bootstrap 菜单 * @param url Current URL * @param hasIcon Take tab logo as icon * @param dropdownClasses Dropdown classes * @returns Component */ createBootstrapMenu( url: string, hasIcon?: boolean, dropdownClasses?: string ) { // Top menu items const tabs = this.data.tabs; const tops = tabs.filter((tab) => tab.parent == null); dropdownClasses ??= 'dropdown-menu shadow-sm fade-up m-0'; return ( {tops.map((t) => { const children = tabs.filter((tab) => tab.parent === t.id); if (children.length === 0) { // Skip if 'not display in menu' if (t.layout === 1) return; return ( {this.createMenuItem(t, hasIcon)} ); } else { return (
{this.createMenuItem(t, hasIcon)}
{children.map((c) => ( {c.name} ))}
); } })}
); } /** * Create Google Analytics service * @returns Component */ createGAService(): React.ReactNode { const ga = this.getService('GA'); if (ga == null) return; return ( ); } /** * Create Google reCAPTCHA service * @param domain Service domain, 'R' for recaptcha.net, 'G' for google.com * @returns Component */ createRECAPService(domain: 'G' | 'R' = 'G'): React.ReactNode { const re = this.getService('RECAP'); if (re == null) return; const api = domain === 'G' ? 'google.com' : 'recaptcha.net'; return ( ); } /** * Create tab list * 创建栏目列表 * @param name Name * @param url URL * @param options Options * @returns Component */ createTabList( name: string, url: string, options?: TabListOptions ): React.ReactElement; createTabList( name: string, tabs: SiteTab[], options?: TabListOptions ): React.ReactElement; createTabList( name: string, urlOrTabs: string | SiteTab[], options?: TabListOptions ) { const tabs = typeof urlOrTabs === 'string' ? this.getListTab((tab) => tab.url === urlOrTabs)[1] : urlOrTabs; const { classNames = 'form-select', emptyOptionLabel = 'Choose...', ...rest } = options ?? {}; return ( ); } /** * Create Wechat service * @param onLoad On load handler * @returns Component */ createWXService(onLoad?: () => void): React.ReactNode { const wx = this.getService('WX'); if (wx == null) return; return (