@coffic/cosy-ui 0.8.29 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,3 @@
1
- /**
2
- * 国际化文本配置
3
- *
4
- * 提供组件的多语言文本内容
5
- */
6
-
7
1
  // 定义文本内容的类型
8
2
  type TextContent = Record<string, Record<string, string>>;
9
3
 
@@ -1,6 +1,4 @@
1
- import { getRelativeLocaleUrl } from 'astro:i18n';
2
1
  import type { AstroGlobal } from 'astro';
3
- import { cosyLogger } from '../../src-astro/cosy';
4
2
 
5
3
  // 默认语言
6
4
  export const DEFAULT_LANGUAGE = 'en';
@@ -11,111 +9,92 @@ export const DEFAULT_LANGUAGE = 'en';
11
9
  * 提供语言相关的工具函数,用于多语言支持
12
10
  */
13
11
  export class LanguageUtil {
14
- static getRelativeLink(locale: string, astro: AstroGlobal): string {
15
- const debug = false;
16
- const currentLocale = astro.currentLocale;
17
- const originalPath = astro.originPathname;
18
- const result = getRelativeLocaleUrl(
19
- locale,
20
- originalPath.replaceAll('/' + currentLocale, '')
21
- );
22
-
23
- if (debug) {
24
- cosyLogger.debug(
25
- `getRelativeLink: locale=${locale}, currentPath=${originalPath}, currentLocale=${currentLocale}, result=${result}`
26
- );
27
- }
28
-
29
- return result;
30
- }
31
-
32
- static getLanguageName(code: string | undefined): string {
33
- switch (code) {
34
- case 'en':
35
- return 'English';
36
- case 'zh-cn':
37
- return '简体中文';
38
- case 'zh':
39
- return '中文';
40
- case undefined:
41
- default:
42
- return 'not known';
12
+ static getLanguageName(code: string | undefined): string {
13
+ switch (code) {
14
+ case 'en':
15
+ return 'English';
16
+ case 'zh-cn':
17
+ return '简体中文';
18
+ case 'zh':
19
+ return '中文';
20
+ default:
21
+ return code || 'not known';
22
+ }
43
23
  }
44
- }
45
24
 
46
- /**
47
- * 获取当前语言
48
- * @param astro Astro全局对象
49
- * @returns 当前应使用的语言代码
50
- */
51
- static getCurrentLanguage(astro: AstroGlobal): string {
52
- // 尝试从Astro全局对象中获取语言
53
- const astroLang = astro.currentLocale;
54
- if (astroLang) {
55
- return astroLang;
56
- }
57
- // 尝试从URL中获取语言
58
- const urlLang = this.getLanguageFromURL(astro.url.pathname);
59
- if (urlLang) {
60
- return urlLang;
61
- }
62
-
63
- // 尝试从浏览器设置中获取语言
64
- const browserLang = this.getLanguageFromBrowser();
65
- if (browserLang) {
66
- return browserLang;
25
+ /**
26
+ * 获取当前语言
27
+ * @param astro Astro全局对象
28
+ * @returns 当前应使用的语言代码
29
+ */
30
+ static getCurrentLanguage(astro: AstroGlobal): string {
31
+ // 尝试从Astro全局对象中获取语言
32
+ const astroLang = astro.currentLocale;
33
+ if (astroLang) {
34
+ return astroLang;
35
+ }
36
+ // 尝试从URL中获取语言
37
+ const urlLang = this.getLanguageFromURL(astro.url.pathname);
38
+ if (urlLang) {
39
+ return urlLang;
40
+ }
41
+
42
+ // 尝试从浏览器设置中获取语言
43
+ const browserLang = this.getLanguageFromBrowser();
44
+ if (browserLang) {
45
+ return browserLang;
46
+ }
47
+
48
+ // 尝试从Astro全局对象中获取语言
49
+ const preferredLocale = astro.preferredLocale;
50
+ if (preferredLocale) {
51
+ return preferredLocale;
52
+ }
53
+
54
+ // 如果无法检测,返回默认语言
55
+ return DEFAULT_LANGUAGE;
67
56
  }
68
57
 
69
- // 尝试从Astro全局对象中获取语言
70
- const preferredLocale = astro.preferredLocale;
71
- if (preferredLocale) {
72
- return preferredLocale;
58
+ /**
59
+ * 从URL中提取语言代码
60
+ * @param url 当前URL
61
+ * @returns 从URL中提取的语言代码,如果无法提取则返回undefined
62
+ */
63
+ private static getLanguageFromURL(url: string): string | undefined {
64
+ let currentUrl = url;
65
+
66
+ if (currentUrl === undefined) {
67
+ if (typeof window === 'undefined') return undefined;
68
+ currentUrl = window.location.href;
69
+ }
70
+
71
+ // 尝试从路径中提取语言代码
72
+ // 例如: /zh-cn/components/button
73
+ const pathMatch = currentUrl.match(/^\/([\w-]+)\//);
74
+ if (pathMatch) {
75
+ return pathMatch[1];
76
+ }
77
+
78
+ // 尝试从查询参数中提取语言代码
79
+ // 例如: ?lang=zh-cn
80
+ const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
81
+ const langParam = urlParams.get('lang');
82
+ if (langParam) {
83
+ return langParam;
84
+ }
85
+
86
+ return undefined;
73
87
  }
74
88
 
75
- // 如果无法检测,返回默认语言
76
- return DEFAULT_LANGUAGE;
77
- }
78
-
79
- /**
80
- * 从URL中提取语言代码
81
- * @param url 当前URL
82
- * @returns 从URL中提取的语言代码,如果无法提取则返回undefined
83
- */
84
- private static getLanguageFromURL(url: string): string | undefined {
85
- let currentUrl = url;
89
+ /**
90
+ * 从浏览器设置中获取首选语言
91
+ * @returns 从浏览器设置中获取的语言代码,如果无法获取则返回undefined
92
+ */
93
+ private static getLanguageFromBrowser(): string | undefined {
94
+ if (typeof navigator === 'undefined') return undefined;
86
95
 
87
- if (currentUrl === undefined) {
88
- if (typeof window === 'undefined') return undefined;
89
- currentUrl = window.location.href;
96
+ // 获取浏览器语言
97
+ const browserLang = navigator.language.toLowerCase();
98
+ return browserLang;
90
99
  }
91
-
92
- // 尝试从路径中提取语言代码
93
- // 例如: /zh-cn/components/button
94
- const pathMatch = currentUrl.match(/^\/([\w-]+)\//);
95
- if (pathMatch) {
96
- return pathMatch[1];
97
- }
98
-
99
- // 尝试从查询参数中提取语言代码
100
- // 例如: ?lang=zh-cn
101
- const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
102
- const langParam = urlParams.get('lang');
103
- if (langParam) {
104
- return langParam;
105
- }
106
-
107
- return undefined;
108
- }
109
-
110
- /**
111
- * 从浏览器设置中获取首选语言
112
- * @returns 从浏览器设置中获取的语言代码,如果无法获取则返回undefined
113
- */
114
- private static getLanguageFromBrowser(): string | undefined {
115
- if (typeof navigator === 'undefined') return undefined;
116
-
117
- // 获取浏览器语言
118
- const browserLang = navigator.language.toLowerCase();
119
- return browserLang;
120
- }
121
100
  }
@@ -12,17 +12,34 @@
12
12
  * 2. 响应式适配 - 在移动端和桌面端都有合适的展现形式
13
13
  * 3. 可定制性 - 支持多种配置选项,适应不同网站的风格和需求
14
14
  * 4. 多语言支持 - 内置语言切换功能,便于构建国际化网站
15
+ *
16
+ * @usage
17
+ * 基本用法:
18
+ * ```astro
19
+ * <Header />
20
+ * ```
21
+ *
22
+ * 启用语言切换功能:
23
+ * ```astro
24
+ * ---
25
+ * import * as astroI18n from 'astro:i18n';
26
+ * ---
27
+ * <Header astroI18n={astroI18n} />
28
+ * ```
29
+ *
30
+ * @props
31
+ * - astroI18n - 完整的 astro:i18n 模块(启用语言切换时需要)
15
32
  */
16
33
  import {
34
+ Image,
17
35
  LanguageSwitcher,
36
+ Link,
18
37
  LinkUtil,
19
38
  type IHeaderProps,
20
39
  type INavItem,
21
40
  NavItems,
22
- Link,
23
- Image,
24
41
  ThemeSwitcher,
25
- } from '@coffic/cosy-ui';
42
+ } from '../../index-astro';
26
43
  import Logo from '../../src/assets/logo-rounded.png';
27
44
 
28
45
  export interface Props extends IHeaderProps {
@@ -31,7 +48,6 @@ export interface Props extends IHeaderProps {
31
48
 
32
49
  const {
33
50
  height = 'md',
34
- languages = ['zh-cn', 'en'],
35
51
  logo = Logo,
36
52
  logoHref = '/',
37
53
  navItems = [],
@@ -42,6 +58,7 @@ const {
42
58
  paddingVertical = 'none',
43
59
  navPosition = 'center',
44
60
  showThemeSwitcher = true,
61
+ astroI18n,
45
62
  } = Astro.props;
46
63
 
47
64
  // 根据高度设置样式
@@ -79,6 +96,10 @@ const logoSizeClasses = {
79
96
 
80
97
  const logoSizeClass = logoSizeClasses[height];
81
98
  const linkHeightClass = linkHeightClasses[height];
99
+
100
+ // 检查 i18n 是否启用,通过 Astro.currentLocale 来判断
101
+ let isI18nEnabled = Astro.currentLocale !== undefined;
102
+
82
103
  const currentPath = Astro.url.pathname;
83
104
  const activeLink = LinkUtil.getActiveLink(
84
105
  currentPath,
@@ -200,7 +221,7 @@ const activeLink = LinkUtil.getActiveLink(
200
221
  }
201
222
 
202
223
  {showThemeSwitcher && <ThemeSwitcher />}
203
- {languages.length > 1 && <LanguageSwitcher languages={languages} />}
224
+ {isI18nEnabled && <LanguageSwitcher astroI18n={astroI18n} />}
204
225
 
205
226
  <slot name="navbar-end" />
206
227
  </div>
@@ -14,55 +14,84 @@
14
14
  * 4. 一致的视觉风格 - 使用与整体设计系统一致的下拉菜单样式
15
15
  *
16
16
  * @usage
17
- * 基本用法:
17
+ * 基本用法(需要用户传入 astro:i18n 模块):
18
18
  * ```astro
19
- * <LanguageSwitcher />
19
+ * ---
20
+ * import * as astroI18n from 'astro:i18n';
21
+ * ---
22
+ * <LanguageSwitcher astroI18n={astroI18n} />
20
23
  * ```
21
24
  *
22
- * 自定义语言列表:
23
- * ```astro
24
- * <LanguageSwitcher
25
- * languages={['zh-cn', 'en']}
26
- * />
27
- * ```
25
+ * @props
26
+ * - astroI18n - 完整的 astro:i18n 模块
27
+ * - class - 自定义CSS类名
28
28
  */
29
29
 
30
- import { ChevronDownIcon, LanguageUtil } from '../../index-astro';
30
+ import { ChevronDownIcon, Link, ListItem } from '../../index-astro';
31
+ import {
32
+ checkSwitcherRenderState,
33
+ generateSwitcherLinks,
34
+ type SwitcherLink,
35
+ } from './switcher_util';
31
36
  import '../../style.ts';
32
37
 
33
38
  interface Props {
34
- languages: string[];
39
+ /**
40
+ * 完整的 astro:i18n 模块
41
+ */
42
+ astroI18n?: any;
43
+
35
44
  /**
36
45
  * 自定义类名
37
46
  */
38
47
  class?: string;
39
48
  }
40
49
 
41
- const { languages = ['zh-cn', 'en'], class: className = '' } = Astro.props;
50
+ const { astroI18n, class: className = '' } = Astro.props;
51
+
52
+ // 检查渲染状态
53
+ const renderState = checkSwitcherRenderState(Astro.currentLocale, astroI18n);
42
54
 
43
- const currentLocale = Astro.currentLocale;
44
- const currentLanguageName = LanguageUtil.getLanguageName(currentLocale);
55
+ // 输出警告信息
56
+ if (renderState.warnings) {
57
+ renderState.warnings.forEach((warning) => console.warn(warning));
58
+ }
59
+
60
+ let links: SwitcherLink[] = [];
61
+
62
+ // 如果应该渲染,生成切换链接
63
+ if (renderState.shouldRender && Astro.currentLocale) {
64
+ try {
65
+ links = generateSwitcherLinks(
66
+ astroI18n,
67
+ Astro.currentLocale,
68
+ Astro.url.pathname
69
+ );
70
+ } catch (error) {
71
+ // 如果生成链接失败,设置为不渲染
72
+ renderState.shouldRender = false;
73
+ }
74
+ }
45
75
  ---
46
76
 
47
- <!-- 语言切换按钮 -->
48
- <div class={`cosy:dropdown cosy:dropdown-end ${className}`}>
49
- <div tabindex="0" role="button" class:list={['cosy:btn cosy:btn-ghost']}>
50
- <span class="cosy:mr-1">{currentLanguageName}</span>
51
- <ChevronDownIcon size="16px" class="cosy:w-4 cosy:h-4" />
52
- </div>
53
- <ul
54
- tabindex="0"
55
- class="cosy:z-[1] cosy:bg-base-100 cosy:shadow cosy:p-2 cosy:rounded-box cosy:w-32 cosy:dropdown-content cosy:menu">
56
- {
57
- languages.map((lang) => (
58
- <li class={currentLocale === lang ? 'cosy:disabled' : ''}>
59
- <a
60
- href={LanguageUtil.getRelativeLink(lang, Astro)}
61
- class={currentLocale === lang ? 'cosy:active' : ''}>
62
- {LanguageUtil.getLanguageName(lang)}
63
- </a>
64
- </li>
65
- ))
66
- }
67
- </ul>
68
- </div>
77
+ {
78
+ renderState.shouldRender && (
79
+ <div class={`cosy:dropdown cosy:dropdown-end ${className}`}>
80
+ <div tabindex="0" role="button" class:list={['cosy:btn cosy:btn-ghost']}>
81
+ <span class="cosy:mr-1">{renderState.currentLanguageName}</span>
82
+ <ChevronDownIcon size="16px" class="cosy:w-4 cosy:h-4" />
83
+ </div>
84
+ <ul
85
+ tabindex="0"
86
+ class="cosy:z-[1] cosy:bg-base-100 cosy:shadow cosy:p-2 cosy:rounded-box cosy:w-32 cosy:dropdown-content cosy:menu">
87
+ {links.map((link) => (
88
+ <ListItem>
89
+ <Link href={link.url} active={Astro.currentLocale === link.locale}>
90
+ {link.name}
91
+ </Link>
92
+ </ListItem>
93
+ ))}
94
+ </ul>
95
+ </div>
96
+ )
97
+ }
@@ -1,11 +1,3 @@
1
1
  import LanguageSwitcher from './LanguageSwitcher.astro';
2
- import LanguageSwitcherBasic from './LanguageSwitcherBasic.astro';
3
- import BasicSourceCode from './LanguageSwitcherBasic.astro?raw';
4
- import { extractSimpleExample } from '../../src/utils/component';
5
2
 
6
- export { LanguageSwitcher, LanguageSwitcherBasic };
7
-
8
- // 导出示例源代码
9
- export const LanguageSwitcherExampleCodes = {
10
- Basic: extractSimpleExample(BasicSourceCode, 'LanguageSwitcher'),
11
- };
3
+ export { LanguageSwitcher };
@@ -0,0 +1,84 @@
1
+ import { LanguageUtil } from "../../src/utils/language"
2
+
3
+ export interface SwitcherLink {
4
+ locale: string;
5
+ name: string;
6
+ url: string;
7
+ }
8
+
9
+ /**
10
+ * 获取基础 URL
11
+ */
12
+ export const getBaseUrl = (): string => {
13
+ return import.meta.env.BASE_URL || '/';
14
+ };
15
+
16
+ /**
17
+ * 从 URL 中提取语言代码
18
+ */
19
+ export const getLocaleFromUrl = (url: string): string => {
20
+ return url.replace(getBaseUrl(), '').split('/')[0];
21
+ };
22
+
23
+ /**
24
+ * 生成语言切换链接
25
+ * @param astroI18n - astro:i18n 模块
26
+ * @param currentLocale - 当前语言代码
27
+ * @param pathname - 当前页面路径
28
+ * @returns 语言切换链接数组
29
+ */
30
+ export const generateSwitcherLinks = (
31
+ astroI18n: any,
32
+ currentLocale: string,
33
+ pathname: string
34
+ ): SwitcherLink[] => {
35
+ try {
36
+ const { getRelativeLocaleUrl, getRelativeLocaleUrlList } = astroI18n;
37
+
38
+ const currentLocalURLPrefix = getRelativeLocaleUrl(currentLocale, '');
39
+ const pathWithSlash = pathname + '/';
40
+ const slug = pathWithSlash.replace(currentLocalURLPrefix, '');
41
+ const urls = getRelativeLocaleUrlList(slug);
42
+
43
+ return urls.map((url: string) => ({
44
+ locale: getLocaleFromUrl(url),
45
+ name: LanguageUtil.getLanguageName(getLocaleFromUrl(url)),
46
+ url: url,
47
+ }));
48
+ } catch (error) {
49
+ console.warn('LanguageSwitcher: Error generating switcher links:', error);
50
+ throw error;
51
+ }
52
+ };
53
+
54
+ /**
55
+ * 检查语言切换器是否应该渲染
56
+ * @param currentLocale - 当前语言代码
57
+ * @param astroI18n - astro:i18n 模块
58
+ * @returns 是否应该渲染的状态信息
59
+ */
60
+ export const checkSwitcherRenderState = (
61
+ currentLocale: string | undefined,
62
+ astroI18n: any
63
+ ): {
64
+ shouldRender: boolean;
65
+ currentLanguageName?: string;
66
+ warnings?: string[];
67
+ } => {
68
+ const warnings: string[] = [];
69
+
70
+ if (!currentLocale) {
71
+ warnings.push('LanguageSwitcher: i18n is not enabled in the current project');
72
+ return { shouldRender: false, warnings };
73
+ }
74
+
75
+ if (!astroI18n) {
76
+ warnings.push('LanguageSwitcher: astroI18n module is required. Please pass the astro:i18n module as a prop.');
77
+ return { shouldRender: false, warnings };
78
+ }
79
+
80
+ return {
81
+ shouldRender: true,
82
+ currentLanguageName: LanguageUtil.getLanguageName(currentLocale),
83
+ };
84
+ };
@@ -115,7 +115,7 @@ const hrefToIconMap: Record<string, string> = {
115
115
  */
116
116
  export function getIconFromHref(href: string, fallbackIcon: string = 'folder'): string {
117
117
  // 将 href 转换为小写并移除路径分隔符
118
- const normalizedHref = href.toLowerCase().replace(/[\/\-_]/g, '');
118
+ const normalizedHref = href.toLowerCase().replace(/[/\-_]/g, '');
119
119
 
120
120
  // 遍历映射表,找到匹配的关键词
121
121
  for (const [keyword, iconName] of Object.entries(hrefToIconMap)) {
@@ -100,6 +100,7 @@ interface Props extends HTMLAttributes<'a'> {
100
100
  | 'warning'
101
101
  | 'error';
102
102
  align?: 'left' | 'center' | 'right';
103
+ active?: boolean;
103
104
  }
104
105
 
105
106
  const {
@@ -121,6 +122,7 @@ const {
121
122
  fullWidth = false,
122
123
  color,
123
124
  align,
125
+ active = false,
124
126
  ...rest
125
127
  } = Astro.props;
126
128
 
@@ -159,7 +161,10 @@ const classes = [
159
161
  animation === 'hover-scale' &&
160
162
  'cosy:hover:scale-105 cosy:transition-transform',
161
163
 
162
- // 新增:按钮风格
164
+ // Active state
165
+ active && 'cosy:active',
166
+
167
+ // 按钮风格
163
168
  btn && 'cosy:btn',
164
169
  btn && size === 'sm' && 'cosy:btn-sm',
165
170
  btn && size === 'lg' && 'cosy:btn-lg',
@@ -1,10 +1,35 @@
1
1
  ---
2
2
  /**
3
3
  * @component ListItem
4
- * @description List item component with loading animations
5
- * @props loading?: boolean
6
- * @props duration?: number
7
- * @props animationType?: string
4
+ *
5
+ * @description
6
+ * ListItem 组件用于在列表中显示单个项目。它内置了多种加载动画效果,可以方便地通过 `loading` 属性来控制加载状态,并通过 `loadingAnimationType` 属性切换不同的加载Loading动画样式。
7
+ *
8
+ * @usage
9
+ * 基本用法:
10
+ * ```astro
11
+ * <ListItem>这是一个列表项</ListItem>
12
+ * ```
13
+ *
14
+ * 加载状态:
15
+ * ```astro
16
+ * <ListItem loading={true}>加载中的列表项</ListItem>
17
+ * ```
18
+ *
19
+ * 指定加载动画类型:
20
+ * ```astro
21
+ * <ListItem loading={true} loadingAnimationType="pulse">使用脉冲动画加载</ListItem>
22
+ * <ListItem loading={true} loadingAnimationType="icon-left">使用左侧图标动画加载</ListItem>
23
+ * ```
24
+ *
25
+ * @props
26
+ * @prop {string} [class] - 自定义 CSS 类名。
27
+ * @prop {boolean} [loading=false] - 控制是否显示加载动画。
28
+ * @prop {number} [duration] - 动画的持续时间(单位:毫秒)。
29
+ * @prop {('none'|'ring'|'icon-left'|'icon-right'|'breath'|'pulse'|'glow')} [loadingAnimationType='none'] - 加载动画的类型。
30
+ *
31
+ * @slots
32
+ * @slot default - 列表项的内容。
8
33
  */
9
34
  import '../../style.ts';
10
35
  import ListItemRing from './ListItemRing.astro';
@@ -14,16 +39,38 @@ import ListItemBreath from './ListItemBreath.astro';
14
39
  import ListItemPulse from './ListItemPulse.astro';
15
40
  import ListItemGlow from './ListItemGlow.astro';
16
41
 
42
+ interface Props {
43
+ class?: string;
44
+ loading?: boolean;
45
+ duration?: number;
46
+ loadingAnimationType?:
47
+ | 'none'
48
+ | 'ring'
49
+ | 'icon-left'
50
+ | 'icon-right'
51
+ | 'breath'
52
+ | 'pulse'
53
+ | 'glow';
54
+ }
55
+
17
56
  const {
18
57
  loading = false,
19
58
  duration,
20
- animationType = 'ring',
59
+ loadingAnimationType = 'none',
21
60
  ...restProps
22
61
  } = Astro.props;
23
62
  ---
24
63
 
25
64
  {
26
- animationType === 'ring' && (
65
+ loadingAnimationType === 'none' && (
66
+ <li {...restProps}>
67
+ <slot />
68
+ </li>
69
+ )
70
+ }
71
+
72
+ {
73
+ loadingAnimationType === 'ring' && (
27
74
  <ListItemRing loading={loading} duration={duration} {...restProps}>
28
75
  <slot />
29
76
  </ListItemRing>
@@ -31,7 +78,7 @@ const {
31
78
  }
32
79
 
33
80
  {
34
- animationType === 'icon-left' && (
81
+ loadingAnimationType === 'icon-left' && (
35
82
  <ListItemIconLeft loading={loading} duration={duration} {...restProps}>
36
83
  <slot />
37
84
  </ListItemIconLeft>
@@ -39,7 +86,7 @@ const {
39
86
  }
40
87
 
41
88
  {
42
- animationType === 'icon-right' && (
89
+ loadingAnimationType === 'icon-right' && (
43
90
  <ListItemIconRight loading={loading} duration={duration} {...restProps}>
44
91
  <slot />
45
92
  </ListItemIconRight>
@@ -47,7 +94,7 @@ const {
47
94
  }
48
95
 
49
96
  {
50
- animationType === 'breath' && (
97
+ loadingAnimationType === 'breath' && (
51
98
  <ListItemBreath loading={loading} duration={duration} {...restProps}>
52
99
  <slot />
53
100
  </ListItemBreath>
@@ -55,7 +102,7 @@ const {
55
102
  }
56
103
 
57
104
  {
58
- animationType === 'pulse' && (
105
+ loadingAnimationType === 'pulse' && (
59
106
  <ListItemPulse loading={loading} duration={duration} {...restProps}>
60
107
  <slot />
61
108
  </ListItemPulse>
@@ -63,7 +110,7 @@ const {
63
110
  }
64
111
 
65
112
  {
66
- animationType === 'glow' && (
113
+ loadingAnimationType === 'glow' && (
67
114
  <ListItemGlow loading={loading} duration={duration} {...restProps}>
68
115
  <slot />
69
116
  </ListItemGlow>
@@ -1,87 +1,88 @@
1
1
  import type { INavItem } from './nav';
2
+ import type { ImageMetadata } from 'astro';
2
3
 
3
4
  export interface IHeaderProps {
4
- /**
5
- * 侧边栏是否默认展开
6
- * @default false
7
- */
8
- defaultSidebarOpen?: boolean;
5
+ /**
6
+ * 侧边栏是否默认展开
7
+ * @default false
8
+ */
9
+ defaultSidebarOpen?: boolean;
9
10
 
10
- /**
11
- * 导航栏高度
12
- * @default "md"
13
- */
14
- height?: '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
11
+ /**
12
+ * 完整的 astro:i18n 模块(启用语言切换时需要)
13
+ */
14
+ astroI18n?: any;
15
15
 
16
- /**
17
- * 语言选项列表
18
- */
19
- languages?: string[];
16
+ /**
17
+ * 导航栏高度
18
+ * @default "md"
19
+ */
20
+ height?: '3xs' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
20
21
 
21
- /**
22
- * Logo图片元数据
23
- */
24
- logo?: ImageMetadata;
22
+ /**
23
+ * Logo图片元数据
24
+ */
25
+ logo?: ImageMetadata;
25
26
 
26
- /**
27
- * Logo 链接地址
28
- * @default "/"
29
- */
30
- logoHref?: string;
27
+ /**
28
+ * Logo 链接地址
29
+ * @default "/"
30
+ */
31
+ logoHref?: string;
31
32
 
32
- /**
33
- * 导航菜单项
34
- */
35
- navItems?: INavItem[];
33
+ /**
34
+ * 导航菜单项
35
+ */
36
+ navItems?: INavItem[];
36
37
 
37
- /**
38
- * 导航栏位置
39
- * @default "start"
40
- */
41
- navPosition?: 'start' | 'center' | 'end';
38
+ /**
39
+ * 导航栏位置
40
+ * @default "start"
41
+ */
42
+ navPosition?: 'start' | 'center' | 'end';
42
43
 
43
- /**
44
- * 水平内边距
45
- * @default "md"
46
- */
47
- paddingHorizontal?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
44
+ /**
45
+ * 水平内边距
46
+ * @default "md"
47
+ */
48
+ paddingHorizontal?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
48
49
 
49
- /**
50
- * 垂直内边距
51
- * @default "md"
52
- */
53
- paddingVertical?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
50
+ /**
51
+ * 垂直内边距
52
+ * @default "md"
53
+ */
54
+ paddingVertical?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
54
55
 
55
- /**
56
- * 圆角大小
57
- * @default "md"
58
- */
59
- rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
56
+ /**
57
+ * 圆角大小
58
+ * @default "md"
59
+ */
60
+ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full';
60
61
 
61
- /**
62
- * 是否显示侧边栏切换按钮
63
- * @default false
64
- */
65
- showSidebarToggle?: boolean;
62
+ /**
63
+ * 是否显示侧边栏切换按钮
64
+ * @default false
65
+ */
66
+ showSidebarToggle?: boolean;
66
67
 
67
- /**
68
- * 是否显示主题切换按钮
69
- * @default false
70
- */
71
- showThemeSwitcher?: boolean;
68
+ /**
69
+ * 是否显示主题切换按钮
70
+ * @default false
71
+ */
72
+ showThemeSwitcher?: boolean;
72
73
 
73
- /**
74
- * 社交媒体链接列表
75
- */
76
- socialLinks?: Array<{
77
- name: string;
78
- url: string;
79
- icon: any;
80
- }>;
74
+ /**
75
+ * 社交媒体链接列表
76
+ */
77
+ socialLinks?: Array<{
78
+ name: string;
79
+ url: string;
80
+ icon: any;
81
+ }>;
81
82
 
82
- /**
83
- * 是否固定在顶部
84
- * @default true
85
- */
86
- sticky?: boolean;
83
+ /**
84
+ * 是否固定在顶部
85
+ * @default true
86
+ */
87
+ sticky?: boolean;
87
88
  }
@@ -5,67 +5,67 @@ import type { IMetaProps } from './meta';
5
5
  import type { ISidebarProps } from './sidebar';
6
6
 
7
7
  export interface IAppLayoutProps {
8
- /**
9
- * 是否显示侧边栏
10
- * @default true
11
- */
12
- showSidebar?: boolean;
8
+ /**
9
+ * 是否显示侧边栏
10
+ * @default true
11
+ */
12
+ showSidebar?: boolean;
13
13
 
14
- /**
15
- * 是否显示页眉
16
- * @default true
17
- */
18
- showHeader?: boolean;
14
+ /**
15
+ * 是否显示页眉
16
+ * @default true
17
+ */
18
+ showHeader?: boolean;
19
19
 
20
- /**
21
- * 是否显示页脚
22
- * @default true
23
- */
24
- showFooter?: boolean;
20
+ /**
21
+ * 是否显示页脚
22
+ * @default true
23
+ */
24
+ showFooter?: boolean;
25
25
 
26
- /**
27
- * 自定义头部内容
28
- */
29
- head?: astroHTML.JSX.Element;
26
+ /**
27
+ * 自定义头部内容
28
+ */
29
+ head?: any;
30
30
 
31
- /**
32
- * 自定义头部内容
33
- */
34
- headerConfig: IHeaderProps;
31
+ /**
32
+ * 自定义头部内容
33
+ */
34
+ headerConfig: IHeaderProps;
35
35
 
36
- /**
37
- * 侧边栏配置
38
- */
39
- sidebarConfig: ISidebarProps;
36
+ /**
37
+ * 侧边栏配置
38
+ */
39
+ sidebarConfig: ISidebarProps;
40
40
 
41
- /**
42
- * 主内容配置
43
- */
44
- mainContentConfig: IMainContentProps;
41
+ /**
42
+ * 主内容配置
43
+ */
44
+ mainContentConfig: IMainContentProps;
45
45
 
46
- /**
47
- * 页面类名
48
- */
49
- class?: string;
46
+ /**
47
+ * 页面类名
48
+ */
49
+ class?: string;
50
50
 
51
- /**
52
- * 类名列表
53
- */
54
- 'class:list'?: any;
51
+ /**
52
+ * 类名列表
53
+ */
54
+ 'class:list'?: any;
55
55
 
56
- /**
57
- * 调试模式,显示各个部分的边框
58
- * @default false
59
- */
60
- debug?: boolean;
56
+ /**
57
+ * 调试模式,显示各个部分的边框
58
+ * @default false
59
+ */
60
+ debug?: boolean;
61
61
 
62
- /**
63
- * 元数据配置
64
- */
65
- metaConfig: IMetaProps;
62
+ /**
63
+ * 元数据配置
64
+ */
65
+ metaConfig: IMetaProps;
66
66
 
67
- /**
68
- * 页脚相关配置
69
- */
70
- footerConfig: IFooterProps;
67
+ /**
68
+ * 页脚相关配置
69
+ */
70
+ footerConfig: IFooterProps;
71
71
  }
@@ -1,3 +1,5 @@
1
+ import type { ImageMetadata } from 'astro';
2
+
1
3
  export interface IMetaProps {
2
4
  title: string;
3
5
  description: string;
@@ -41,7 +43,7 @@ export interface IMetaProps {
41
43
  /**
42
44
  * 自定义头部内容
43
45
  */
44
- head?: astroHTML.JSX.Element;
46
+ head?: any;
45
47
 
46
48
  /**
47
49
  * 页面类名
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffic/cosy-ui",
3
- "version": "0.8.29",
3
+ "version": "0.9.2",
4
4
  "description": "An astro component library",
5
5
  "author": {
6
6
  "name": "nookery",
@@ -1,7 +0,0 @@
1
- ---
2
- import LanguageSwitcher from './LanguageSwitcher.astro';
3
- ---
4
-
5
- <div class="cosy:flex cosy:justify-center">
6
- <LanguageSwitcher languages={['zh-cn', 'en']} />
7
- </div>
@@ -1,121 +0,0 @@
1
- import { getRelativeLocaleUrl } from 'astro:i18n';
2
- import type { AstroGlobal } from 'astro';
3
- import { cosyLogger } from '../cosy';
4
-
5
- // 默认语言
6
- export const DEFAULT_LANGUAGE = 'en';
7
-
8
- /**
9
- * 语言工具模块
10
- *
11
- * 提供语言相关的工具函数,用于多语言支持
12
- */
13
- export class LanguageUtil {
14
- static getRelativeLink(locale: string, astro: AstroGlobal): string {
15
- const debug = false;
16
- const currentLocale = astro.currentLocale;
17
- const originalPath = astro.originPathname;
18
- const result = getRelativeLocaleUrl(
19
- locale,
20
- originalPath.replaceAll('/' + currentLocale, '')
21
- );
22
-
23
- if (debug) {
24
- cosyLogger.debug(
25
- `getRelativeLink: locale=${locale}, currentPath=${originalPath}, currentLocale=${currentLocale}, result=${result}`
26
- );
27
- }
28
-
29
- return result;
30
- }
31
-
32
- static getLanguageName(code: string | undefined): string {
33
- switch (code) {
34
- case 'en':
35
- return 'English';
36
- case 'zh-cn':
37
- return '简体中文';
38
- case 'zh':
39
- return '中文';
40
- case undefined:
41
- default:
42
- return 'not known';
43
- }
44
- }
45
-
46
- /**
47
- * 获取当前语言
48
- * @param astro Astro全局对象
49
- * @returns 当前应使用的语言代码
50
- */
51
- static getCurrentLanguage(astro: AstroGlobal): string {
52
- // 尝试从Astro全局对象中获取语言
53
- const astroLang = astro.currentLocale;
54
- if (astroLang) {
55
- return astroLang;
56
- }
57
- // 尝试从URL中获取语言
58
- const urlLang = this.getLanguageFromURL(astro.url.pathname);
59
- if (urlLang) {
60
- return urlLang;
61
- }
62
-
63
- // 尝试从浏览器设置中获取语言
64
- const browserLang = this.getLanguageFromBrowser();
65
- if (browserLang) {
66
- return browserLang;
67
- }
68
-
69
- // 尝试从Astro全局对象中获取语言
70
- const preferredLocale = astro.preferredLocale;
71
- if (preferredLocale) {
72
- return preferredLocale;
73
- }
74
-
75
- // 如果无法检测,返回默认语言
76
- return DEFAULT_LANGUAGE;
77
- }
78
-
79
- /**
80
- * 从URL中提取语言代码
81
- * @param url 当前URL
82
- * @returns 从URL中提取的语言代码,如果无法提取则返回undefined
83
- */
84
- private static getLanguageFromURL(url: string): string | undefined {
85
- let currentUrl = url;
86
-
87
- if (currentUrl === undefined) {
88
- if (typeof window === 'undefined') return undefined;
89
- currentUrl = window.location.href;
90
- }
91
-
92
- // 尝试从路径中提取语言代码
93
- // 例如: /zh-cn/components/button
94
- const pathMatch = currentUrl.match(/^\/([\w-]+)\//);
95
- if (pathMatch) {
96
- return pathMatch[1];
97
- }
98
-
99
- // 尝试从查询参数中提取语言代码
100
- // 例如: ?lang=zh-cn
101
- const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
102
- const langParam = urlParams.get('lang');
103
- if (langParam) {
104
- return langParam;
105
- }
106
-
107
- return undefined;
108
- }
109
-
110
- /**
111
- * 从浏览器设置中获取首选语言
112
- * @returns 从浏览器设置中获取的语言代码,如果无法获取则返回undefined
113
- */
114
- private static getLanguageFromBrowser(): string | undefined {
115
- if (typeof navigator === 'undefined') return undefined;
116
-
117
- // 获取浏览器语言
118
- const browserLang = navigator.language.toLowerCase();
119
- return browserLang;
120
- }
121
- }