@uniweb/kit 0.4.12 → 0.5.0
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.
- package/package.json +4 -3
- package/src/components/DataPlaceholder.jsx +28 -0
- package/src/components/Icon/Icon.jsx +7 -5
- package/src/index.js +4 -0
- package/src/theme-tokens.css +97 -0
- package/src/utils/index.js +59 -0
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Standard component library for Uniweb foundations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.js",
|
|
8
8
|
"./styled": "./src/styled/index.js",
|
|
9
9
|
"./styles": "./src/styles/index.css",
|
|
10
|
-
"./search": "./src/search/index.js"
|
|
10
|
+
"./search": "./src/search/index.js",
|
|
11
|
+
"./theme-tokens.css": "./src/theme-tokens.css"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"src",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"fuse.js": "^7.0.0",
|
|
40
41
|
"shiki": "^3.0.0",
|
|
41
42
|
"tailwind-merge": "^2.6.0",
|
|
42
|
-
"@uniweb/core": "0.
|
|
43
|
+
"@uniweb/core": "0.4.0"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataPlaceholder
|
|
3
|
+
*
|
|
4
|
+
* Default loading UI for components waiting on runtime data.
|
|
5
|
+
* Renders an animated pulse placeholder.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { DataPlaceholder } from '@uniweb/kit'
|
|
9
|
+
*
|
|
10
|
+
* function ArticleList({ content, block }) {
|
|
11
|
+
* if (block.dataLoading) return <DataPlaceholder />
|
|
12
|
+
* return <ArticleGrid articles={content.data.articles} />
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
export function DataPlaceholder({ lines = 3, className = '' }) {
|
|
16
|
+
return (
|
|
17
|
+
<div className={`animate-pulse space-y-4 py-8 ${className}`} role="status" aria-label="Loading">
|
|
18
|
+
{Array.from({ length: lines }, (_, i) => (
|
|
19
|
+
<div key={i} className="h-4 rounded" style={{
|
|
20
|
+
backgroundColor: 'var(--border, #e5e7eb)',
|
|
21
|
+
width: i === lines - 1 ? '60%' : '100%',
|
|
22
|
+
}} />
|
|
23
|
+
))}
|
|
24
|
+
</div>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default DataPlaceholder
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import React, { useState, useEffect, useMemo } from 'react'
|
|
14
14
|
import { getUniweb } from '@uniweb/core'
|
|
15
|
-
import { cn } from '../../utils/index.js'
|
|
15
|
+
import { cn, parseIconRef } from '../../utils/index.js'
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Built-in demo icons (simple SVG paths)
|
|
@@ -138,11 +138,13 @@ export function Icon({
|
|
|
138
138
|
errorComponent,
|
|
139
139
|
...props
|
|
140
140
|
}) {
|
|
141
|
-
// Normalize props
|
|
142
|
-
|
|
143
|
-
const
|
|
141
|
+
// Normalize props — handle icon as string ref ("lu-house", "lu:house"),
|
|
142
|
+
// object ({ library, name }), or URL
|
|
143
|
+
const parsedRef = typeof icon === 'string' ? parseIconRef(icon) : null
|
|
144
|
+
const iconLibrary = library || parsedRef?.library || (typeof icon === 'object' ? icon.library : null)
|
|
145
|
+
const iconName = name || parsedRef?.name || (typeof icon === 'object' ? icon.name : null)
|
|
146
|
+
const iconUrl = url || (!parsedRef && typeof icon === 'string' ? icon : icon?.url)
|
|
144
147
|
const iconSvg = svg || (typeof icon === 'object' ? icon.svg : null)
|
|
145
|
-
const iconName = name || (typeof icon === 'object' ? icon.name : null)
|
|
146
148
|
|
|
147
149
|
// Check sync cache for SSR (pre-populated by prerender)
|
|
148
150
|
const cachedSvg = useMemo(() => {
|
package/src/index.js
CHANGED
|
@@ -46,6 +46,9 @@ export { MediaIcon } from './components/MediaIcon/index.js'
|
|
|
46
46
|
// Files (plain version - for styled card, use @uniweb/kit/tailwind)
|
|
47
47
|
export { Asset } from './components/Asset/index.js'
|
|
48
48
|
|
|
49
|
+
// Data loading
|
|
50
|
+
export { DataPlaceholder } from './components/DataPlaceholder.jsx'
|
|
51
|
+
|
|
49
52
|
// Social
|
|
50
53
|
export {
|
|
51
54
|
SocialIcon,
|
|
@@ -95,6 +98,7 @@ export {
|
|
|
95
98
|
isExternalUrl,
|
|
96
99
|
isFileUrl,
|
|
97
100
|
detectMediaType,
|
|
101
|
+
parseIconRef,
|
|
98
102
|
// Locale utilities
|
|
99
103
|
LOCALE_DISPLAY_NAMES,
|
|
100
104
|
getLocaleLabel
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standard Theme Tokens for Tailwind v4
|
|
3
|
+
*
|
|
4
|
+
* Import this in your foundation's styles.css to register theme CSS variables
|
|
5
|
+
* with Tailwind, enabling utility classes like `text-heading`, `bg-surface`, etc.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* @import "tailwindcss";
|
|
9
|
+
* @import "@uniweb/kit/theme-tokens.css";
|
|
10
|
+
*
|
|
11
|
+
* The actual values come from the build system's theme CSS (injected at runtime).
|
|
12
|
+
* The build generates short names (--primary-600, --heading) and this file bridges
|
|
13
|
+
* them to the --color-* namespace that Tailwind v4 expects for utility classes.
|
|
14
|
+
*
|
|
15
|
+
* Foundations that want custom Tailwind names can skip this import and declare
|
|
16
|
+
* their own @theme inline block.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
@theme inline {
|
|
20
|
+
/* ── Semantic tokens ── */
|
|
21
|
+
/* Values come from theme CSS context classes (light/medium/dark) */
|
|
22
|
+
--color-heading: var(--heading);
|
|
23
|
+
--color-surface: var(--bg);
|
|
24
|
+
--color-surface-subtle: var(--bg-subtle);
|
|
25
|
+
--color-surface-muted: var(--bg-muted);
|
|
26
|
+
--color-body: var(--text);
|
|
27
|
+
--color-muted: var(--text-muted);
|
|
28
|
+
--color-subtle: var(--text-subtle);
|
|
29
|
+
--color-link: var(--link);
|
|
30
|
+
--color-link-hover: var(--link-hover);
|
|
31
|
+
--color-edge: var(--border);
|
|
32
|
+
--color-edge-muted: var(--border-muted);
|
|
33
|
+
--color-ring: var(--ring);
|
|
34
|
+
--color-btn-primary: var(--btn-primary-bg);
|
|
35
|
+
--color-btn-primary-text: var(--btn-primary-text);
|
|
36
|
+
--color-btn-primary-hover: var(--btn-primary-hover);
|
|
37
|
+
--color-btn-secondary: var(--btn-secondary-bg);
|
|
38
|
+
--color-btn-secondary-text: var(--btn-secondary-text);
|
|
39
|
+
--color-btn-secondary-hover: var(--btn-secondary-hover);
|
|
40
|
+
|
|
41
|
+
/* ── Primary palette ── */
|
|
42
|
+
--color-primary-50: var(--primary-50, #eff6ff);
|
|
43
|
+
--color-primary-100: var(--primary-100, #dbeafe);
|
|
44
|
+
--color-primary-200: var(--primary-200, #bfdbfe);
|
|
45
|
+
--color-primary-300: var(--primary-300, #93c5fd);
|
|
46
|
+
--color-primary-400: var(--primary-400, #60a5fa);
|
|
47
|
+
--color-primary-500: var(--primary-500, #3b82f6);
|
|
48
|
+
--color-primary-600: var(--primary-600, #2563eb);
|
|
49
|
+
--color-primary-700: var(--primary-700, #1d4ed8);
|
|
50
|
+
--color-primary-800: var(--primary-800, #1e40af);
|
|
51
|
+
--color-primary-900: var(--primary-900, #1e3a8a);
|
|
52
|
+
--color-primary-950: var(--primary-950, #172554);
|
|
53
|
+
|
|
54
|
+
/* ── Secondary palette ── */
|
|
55
|
+
--color-secondary-50: var(--secondary-50, #f8fafc);
|
|
56
|
+
--color-secondary-100: var(--secondary-100, #f1f5f9);
|
|
57
|
+
--color-secondary-200: var(--secondary-200, #e2e8f0);
|
|
58
|
+
--color-secondary-300: var(--secondary-300, #cbd5e1);
|
|
59
|
+
--color-secondary-400: var(--secondary-400, #94a3b8);
|
|
60
|
+
--color-secondary-500: var(--secondary-500, #64748b);
|
|
61
|
+
--color-secondary-600: var(--secondary-600, #475569);
|
|
62
|
+
--color-secondary-700: var(--secondary-700, #334155);
|
|
63
|
+
--color-secondary-800: var(--secondary-800, #1e293b);
|
|
64
|
+
--color-secondary-900: var(--secondary-900, #0f172a);
|
|
65
|
+
--color-secondary-950: var(--secondary-950, #020617);
|
|
66
|
+
|
|
67
|
+
/* ── Accent palette ── */
|
|
68
|
+
--color-accent-50: var(--accent-50, #f5f3ff);
|
|
69
|
+
--color-accent-100: var(--accent-100, #ede9fe);
|
|
70
|
+
--color-accent-200: var(--accent-200, #ddd6fe);
|
|
71
|
+
--color-accent-300: var(--accent-300, #c4b5fd);
|
|
72
|
+
--color-accent-400: var(--accent-400, #a78bfa);
|
|
73
|
+
--color-accent-500: var(--accent-500, #8b5cf6);
|
|
74
|
+
--color-accent-600: var(--accent-600, #7c3aed);
|
|
75
|
+
--color-accent-700: var(--accent-700, #6d28d9);
|
|
76
|
+
--color-accent-800: var(--accent-800, #5b21b6);
|
|
77
|
+
--color-accent-900: var(--accent-900, #4c1d95);
|
|
78
|
+
--color-accent-950: var(--accent-950, #2e1065);
|
|
79
|
+
|
|
80
|
+
/* ── Neutral palette ── */
|
|
81
|
+
--color-neutral-50: var(--neutral-50, #fafafa);
|
|
82
|
+
--color-neutral-100: var(--neutral-100, #f4f4f5);
|
|
83
|
+
--color-neutral-200: var(--neutral-200, #e4e4e7);
|
|
84
|
+
--color-neutral-300: var(--neutral-300, #d4d4d8);
|
|
85
|
+
--color-neutral-400: var(--neutral-400, #a1a1aa);
|
|
86
|
+
--color-neutral-500: var(--neutral-500, #71717a);
|
|
87
|
+
--color-neutral-600: var(--neutral-600, #52525b);
|
|
88
|
+
--color-neutral-700: var(--neutral-700, #3f3f46);
|
|
89
|
+
--color-neutral-800: var(--neutral-800, #27272a);
|
|
90
|
+
--color-neutral-900: var(--neutral-900, #18181b);
|
|
91
|
+
--color-neutral-950: var(--neutral-950, #09090b);
|
|
92
|
+
|
|
93
|
+
/* ── Brand shorthands ── */
|
|
94
|
+
--color-primary: var(--primary-500, #3b82f6);
|
|
95
|
+
--color-secondary: var(--secondary-500, #64748b);
|
|
96
|
+
--color-accent: var(--accent-500, #8b5cf6);
|
|
97
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -78,6 +78,65 @@ export function getLocaleLabel(locale) {
|
|
|
78
78
|
return locale.label || LOCALE_DISPLAY_NAMES[locale.code] || locale.code.toUpperCase()
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// ─────────────────────────────────────────────────────────────────
|
|
82
|
+
// Icon Utilities
|
|
83
|
+
// ─────────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Short icon family codes (2-3 chars) used for dash-format parsing.
|
|
87
|
+
* Matches the content-reader's ICON_FAMILIES_SHORT list.
|
|
88
|
+
*/
|
|
89
|
+
const ICON_SHORT_CODES = [
|
|
90
|
+
'lu', 'hi', 'hi2', 'pi', 'tb', 'fi', 'bs', 'md', 'ai',
|
|
91
|
+
'ri', 'si', 'io5', 'bi', 'vsc', 'wi', 'gi', 'fa', 'fa6'
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
const ICON_SHORT_CODE_SET = new Set(ICON_SHORT_CODES)
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Parse an icon reference string into { library, name }.
|
|
98
|
+
*
|
|
99
|
+
* Accepts all standard icon formats:
|
|
100
|
+
* - Dash format: "lu-house", "hi2-arrow-right"
|
|
101
|
+
* - Colon format: "lu:house", "lucide:house"
|
|
102
|
+
*
|
|
103
|
+
* Returns null if the string doesn't match any known format.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} ref - Icon reference string
|
|
106
|
+
* @returns {{ library: string, name: string } | null}
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* parseIconRef('lu-house') // { library: 'lu', name: 'house' }
|
|
110
|
+
* parseIconRef('lu:house') // { library: 'lu', name: 'house' }
|
|
111
|
+
* parseIconRef('lucide:house') // { library: 'lucide', name: 'house' }
|
|
112
|
+
* parseIconRef('not-an-icon') // null
|
|
113
|
+
*/
|
|
114
|
+
export function parseIconRef(ref) {
|
|
115
|
+
if (!ref || typeof ref !== 'string') return null
|
|
116
|
+
|
|
117
|
+
// Colon format: "lu:house", "lucide:house"
|
|
118
|
+
const colonIdx = ref.indexOf(':')
|
|
119
|
+
if (colonIdx > 0) {
|
|
120
|
+
return { library: ref.slice(0, colonIdx), name: ref.slice(colonIdx + 1) }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Dash format: "lu-house" — only short codes (2-3 chars) to avoid
|
|
124
|
+
// ambiguity with regular hyphenated strings
|
|
125
|
+
const dashIdx = ref.indexOf('-')
|
|
126
|
+
if (dashIdx > 0) {
|
|
127
|
+
const prefix = ref.slice(0, dashIdx)
|
|
128
|
+
if (ICON_SHORT_CODE_SET.has(prefix)) {
|
|
129
|
+
return { library: prefix, name: ref.slice(dashIdx + 1) }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─────────────────────────────────────────────────────────────────
|
|
137
|
+
// Class / String Utilities
|
|
138
|
+
// ─────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
81
140
|
/**
|
|
82
141
|
* Merge class names with Tailwind CSS conflict resolution
|
|
83
142
|
* @param {...string} classes - Class names to merge
|