@windrun-huaiin/third-ui 5.10.3 → 5.11.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/dist/fuma/mdx/index.d.mts +2 -2
- package/dist/fuma/mdx/index.d.ts +2 -2
- package/dist/fuma/mdx/index.js +214 -93
- package/dist/fuma/mdx/index.js.map +1 -1
- package/dist/fuma/mdx/index.mjs +213 -92
- package/dist/fuma/mdx/index.mjs.map +1 -1
- package/dist/fuma/server.js +44 -58
- package/dist/fuma/server.js.map +1 -1
- package/dist/fuma/server.mjs +41 -55
- package/dist/fuma/server.mjs.map +1 -1
- package/dist/fuma.css +2 -35
- package/dist/main/index.js +5 -39
- package/dist/main/index.js.map +1 -1
- package/dist/main/index.mjs +4 -38
- package/dist/main/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/fuma/mdx/banner.tsx +175 -0
- package/src/fuma/mdx/fuma-banner-suit.tsx +22 -7
- package/src/main/ai-prompt-textarea.tsx +259 -0
- package/src/styles/fuma.css +2 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windrun-huaiin/third-ui",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.11.0",
|
|
4
4
|
"description": "Third-party integrated UI components for windrun-huaiin projects",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"fumadocs-mdx": "11.6.3",
|
|
50
50
|
"fumadocs-typescript": "4.0.4",
|
|
51
51
|
"fumadocs-ui": "15.3.3",
|
|
52
|
+
"class-variance-authority": "^0.7.1",
|
|
52
53
|
"katex": "^0.16.22",
|
|
53
54
|
"mermaid": "^11.6.0",
|
|
54
55
|
"react-medium-image-zoom": "^5.2.14",
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { type HTMLAttributes, useEffect, useState } from 'react';
|
|
5
|
+
import { globalLucideIcons as icons } from '@base-ui/components/global-icon';
|
|
6
|
+
import { cn } from '@lib/utils';
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
'inline-flex items-center justify-center rounded-md p-2 text-sm font-medium transition-colors duration-100 disabled:pointer-events-none disabled:opacity-50',
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
color: {
|
|
13
|
+
primary:
|
|
14
|
+
'bg-primary text-primary-foreground hover:bg-primary/80',
|
|
15
|
+
outline: 'border hover:bg-accent hover:text-accent-foreground',
|
|
16
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
17
|
+
secondary:
|
|
18
|
+
'border bg-secondary text-secondary-foreground hover:bg-accent hover:text-accent-foreground',
|
|
19
|
+
},
|
|
20
|
+
size: {
|
|
21
|
+
sm: 'gap-1 px-2 py-1.5 text-xs',
|
|
22
|
+
icon: 'p-1.5 [&_svg]:size-5',
|
|
23
|
+
'icon-sm': 'p-1.5 [&_svg]:size-4.5',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export function Banner({
|
|
30
|
+
id,
|
|
31
|
+
variant = 'rainbow',
|
|
32
|
+
changeLayout = true,
|
|
33
|
+
height = 3,
|
|
34
|
+
...props
|
|
35
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
36
|
+
/**
|
|
37
|
+
* @defaultValue default is 3 rem
|
|
38
|
+
*/
|
|
39
|
+
height?: number;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @defaultValue 'normal'
|
|
43
|
+
*/
|
|
44
|
+
variant?: 'rainbow' | 'normal';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Change Fumadocs layout styles
|
|
48
|
+
*
|
|
49
|
+
* @defaultValue true
|
|
50
|
+
*/
|
|
51
|
+
changeLayout?: boolean;
|
|
52
|
+
}) {
|
|
53
|
+
const [open, setOpen] = useState(true);
|
|
54
|
+
const globalKey = id ? `nd-banner-${id}` : null;
|
|
55
|
+
const bannerHeight = `${height}rem`;
|
|
56
|
+
const headerStartHeight = `${height + 5.5}rem`;
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (globalKey) setOpen(localStorage.getItem(globalKey) !== 'true');
|
|
60
|
+
}, [globalKey]);
|
|
61
|
+
|
|
62
|
+
if (!open) return null;
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div
|
|
66
|
+
id={id}
|
|
67
|
+
{...props}
|
|
68
|
+
className={cn(
|
|
69
|
+
'flex flex-row items-center justify-center px-4 text-center text-sm font-medium',
|
|
70
|
+
'bg-neutral-100 dark:bg-neutral-900',
|
|
71
|
+
!open && 'hidden',
|
|
72
|
+
props.className,
|
|
73
|
+
)}
|
|
74
|
+
style={{
|
|
75
|
+
// 将 fuma.css 中的 .sticky.top-0.z-40 样式完全移到这里
|
|
76
|
+
position: 'fixed',
|
|
77
|
+
top: 0,
|
|
78
|
+
left: 0,
|
|
79
|
+
width: '100vw',
|
|
80
|
+
zIndex: 1001,
|
|
81
|
+
height: bannerHeight,
|
|
82
|
+
minHeight: bannerHeight,
|
|
83
|
+
maxHeight: bannerHeight,
|
|
84
|
+
margin: 0,
|
|
85
|
+
borderRadius: 0,
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
|
|
89
|
+
{globalKey ? (
|
|
90
|
+
<style>{`.${globalKey} #${id} { display: none; }`}</style>
|
|
91
|
+
) : null}
|
|
92
|
+
{globalKey ? (
|
|
93
|
+
<script
|
|
94
|
+
dangerouslySetInnerHTML={{
|
|
95
|
+
__html: `if (localStorage.getItem('${globalKey}') === 'true') document.documentElement.classList.add('${globalKey}');`,
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
98
|
+
) : null}
|
|
99
|
+
|
|
100
|
+
{variant === 'rainbow' ? rainbowLayer : null}
|
|
101
|
+
{props.children}
|
|
102
|
+
{id ? (
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
aria-label="Close Banner"
|
|
106
|
+
onClick={() => {
|
|
107
|
+
setOpen(false);
|
|
108
|
+
if (globalKey) localStorage.setItem(globalKey, 'true');
|
|
109
|
+
}}
|
|
110
|
+
className={cn(
|
|
111
|
+
buttonVariants({
|
|
112
|
+
color: 'ghost',
|
|
113
|
+
className:
|
|
114
|
+
'absolute end-2 top-1/2 -translate-y-1/2 text-neutral-600 dark:text-neutral-400',
|
|
115
|
+
size: 'icon',
|
|
116
|
+
}),
|
|
117
|
+
)}
|
|
118
|
+
>
|
|
119
|
+
<icons.X />
|
|
120
|
+
</button>
|
|
121
|
+
) : null}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const maskImage =
|
|
127
|
+
'linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)';
|
|
128
|
+
|
|
129
|
+
const rainbowLayer = (
|
|
130
|
+
<>
|
|
131
|
+
<div
|
|
132
|
+
className="absolute inset-0 z-[-1]"
|
|
133
|
+
style={
|
|
134
|
+
{
|
|
135
|
+
maskImage,
|
|
136
|
+
maskComposite: 'intersect',
|
|
137
|
+
animation: 'fd-moving-banner 16s linear infinite',
|
|
138
|
+
'--start': 'rgba(0,87,255,0.5)',
|
|
139
|
+
'--mid': 'rgba(255,0,166,0.77)',
|
|
140
|
+
'--end': 'rgba(255,77,0,0.4)',
|
|
141
|
+
'--via': 'rgba(164,255,68,0.4)',
|
|
142
|
+
animationDirection: 'reverse',
|
|
143
|
+
backgroundImage:
|
|
144
|
+
'repeating-linear-gradient(60deg, var(--end), var(--start) 2%, var(--start) 5%, transparent 8%, transparent 14%, var(--via) 18%, var(--via) 22%, var(--mid) 28%, var(--mid) 30%, var(--via) 34%, var(--via) 36%, transparent, var(--end) calc(50% - 12px))',
|
|
145
|
+
backgroundSize: '200% 100%',
|
|
146
|
+
mixBlendMode: 'difference',
|
|
147
|
+
} as object
|
|
148
|
+
}
|
|
149
|
+
/>
|
|
150
|
+
<div
|
|
151
|
+
className="absolute inset-0 z-[-1]"
|
|
152
|
+
style={
|
|
153
|
+
{
|
|
154
|
+
maskImage,
|
|
155
|
+
maskComposite: 'intersect',
|
|
156
|
+
animation: 'fd-moving-banner 20s linear infinite',
|
|
157
|
+
'--start': 'rgba(255,120,120,0.5)',
|
|
158
|
+
'--mid': 'rgba(36,188,255,0.4)',
|
|
159
|
+
'--end': 'rgba(64,0,255,0.51)',
|
|
160
|
+
'--via': 'rgba(255,89,0,0.56)',
|
|
161
|
+
backgroundImage:
|
|
162
|
+
'repeating-linear-gradient(45deg, var(--end), var(--start) 4%, var(--start) 8%, transparent 9%, transparent 14%, var(--mid) 16%, var(--mid) 20%, transparent, var(--via) 36%, var(--via) 40%, transparent 42%, var(--end) 46%, var(--end) calc(50% - 16.8px))',
|
|
163
|
+
backgroundSize: '200% 100%',
|
|
164
|
+
mixBlendMode: 'color-dodge',
|
|
165
|
+
} as object
|
|
166
|
+
}
|
|
167
|
+
/>
|
|
168
|
+
<style>
|
|
169
|
+
{`@keyframes fd-moving-banner {
|
|
170
|
+
from { background-position: 0% 0; }
|
|
171
|
+
to { background-position: 100% 0; }
|
|
172
|
+
}`}
|
|
173
|
+
</style>
|
|
174
|
+
</>
|
|
175
|
+
);
|
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { Banner } from '
|
|
3
|
+
import { Banner } from './banner';
|
|
4
4
|
import { useTranslations } from 'next-intl';
|
|
5
|
+
import { cn } from '@lib/utils';
|
|
5
6
|
|
|
6
|
-
export function FumaBannerSuit({
|
|
7
|
+
export function FumaBannerSuit({ showBanner }: { showBanner: boolean }) {
|
|
7
8
|
const t = useTranslations('home');
|
|
9
|
+
const heightValue = showBanner ? 3 : 0.5;
|
|
10
|
+
const height= `${heightValue}rem`;
|
|
8
11
|
return (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
<>
|
|
13
|
+
{/* 设置 header 的 top 位置为 Banner 的底部,避免间隙 */}
|
|
14
|
+
{showBanner ? (
|
|
15
|
+
<Banner variant="rainbow" changeLayout={true} height={heightValue}>
|
|
16
|
+
<p className="text-xl">{t('banner')}</p>
|
|
17
|
+
</Banner>
|
|
18
|
+
) : (
|
|
19
|
+
<div
|
|
20
|
+
className="fixed top-0 left-0 w-screen z-[1001] m-0 rounded-none bg-neutral-100 dark:bg-neutral-900"
|
|
21
|
+
style={{
|
|
22
|
+
height: height,
|
|
23
|
+
minHeight: height,
|
|
24
|
+
maxHeight: height,
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
)}
|
|
28
|
+
</>
|
|
14
29
|
);
|
|
15
30
|
}
|
|
16
31
|
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
|
|
5
|
+
interface AIPromptTextareaProps {
|
|
6
|
+
/**
|
|
7
|
+
* Textarea value reference
|
|
8
|
+
*/
|
|
9
|
+
value: string
|
|
10
|
+
/**
|
|
11
|
+
* Textarea value change handler
|
|
12
|
+
*/
|
|
13
|
+
onChange: (value: string) => void
|
|
14
|
+
/**
|
|
15
|
+
* Word limit value reference
|
|
16
|
+
*/
|
|
17
|
+
isWordLimit: boolean
|
|
18
|
+
/**
|
|
19
|
+
* Word limit value change handler
|
|
20
|
+
*/
|
|
21
|
+
onWordLimitChange: (isLimit: boolean) => void
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder
|
|
24
|
+
*/
|
|
25
|
+
placeholder?: string
|
|
26
|
+
/**
|
|
27
|
+
* Disabled switch condition, default is false
|
|
28
|
+
*/
|
|
29
|
+
disabled?: boolean
|
|
30
|
+
/**
|
|
31
|
+
* Maximum words
|
|
32
|
+
*/
|
|
33
|
+
maxWords?: number
|
|
34
|
+
/**
|
|
35
|
+
* Word count unit title
|
|
36
|
+
*/
|
|
37
|
+
wordUnitTitle?: string
|
|
38
|
+
/**
|
|
39
|
+
* Minimum height, px
|
|
40
|
+
*/
|
|
41
|
+
minHeight?: number
|
|
42
|
+
/**
|
|
43
|
+
* Maximum height, px
|
|
44
|
+
*/
|
|
45
|
+
maxHeight?: number
|
|
46
|
+
/**
|
|
47
|
+
* Word count switch, default is true
|
|
48
|
+
*/
|
|
49
|
+
showWordCount?: boolean
|
|
50
|
+
/**
|
|
51
|
+
* Auto scroll switch, default is true
|
|
52
|
+
*/
|
|
53
|
+
autoScroll?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* Extra scroll space, px
|
|
56
|
+
*/
|
|
57
|
+
extraScrollSpace?: number
|
|
58
|
+
/**
|
|
59
|
+
* Custome tailwindcss style
|
|
60
|
+
*/
|
|
61
|
+
className?: string
|
|
62
|
+
/**
|
|
63
|
+
* Title text, if not provided, no title will be rendered
|
|
64
|
+
*/
|
|
65
|
+
title?: string
|
|
66
|
+
/**
|
|
67
|
+
* Description text
|
|
68
|
+
*/
|
|
69
|
+
description?: string
|
|
70
|
+
/**
|
|
71
|
+
* Embed title inside textarea, default is false
|
|
72
|
+
*/
|
|
73
|
+
embed?: boolean
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function AIPromptTextarea({
|
|
77
|
+
value,
|
|
78
|
+
onChange,
|
|
79
|
+
placeholder = "Enter your prompt...",
|
|
80
|
+
disabled = false,
|
|
81
|
+
maxWords = 400,
|
|
82
|
+
wordUnitTitle = "words",
|
|
83
|
+
minHeight = 150,
|
|
84
|
+
maxHeight = 300,
|
|
85
|
+
className = "",
|
|
86
|
+
showWordCount = true,
|
|
87
|
+
autoScroll = true,
|
|
88
|
+
extraScrollSpace = 100,
|
|
89
|
+
isWordLimit,
|
|
90
|
+
onWordLimitChange,
|
|
91
|
+
title,
|
|
92
|
+
description,
|
|
93
|
+
embed = false
|
|
94
|
+
}: AIPromptTextareaProps) {
|
|
95
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
|
96
|
+
|
|
97
|
+
// count words
|
|
98
|
+
const wordArray = value.trim().split(/\s+/).filter(Boolean)
|
|
99
|
+
const wordCount = wordArray.length
|
|
100
|
+
|
|
101
|
+
// auto adjust textarea height
|
|
102
|
+
const adjustTextareaHeight = () => {
|
|
103
|
+
if (textareaRef.current) {
|
|
104
|
+
const textarea = textareaRef.current
|
|
105
|
+
const oldHeight = textarea.style.height
|
|
106
|
+
|
|
107
|
+
// reset height
|
|
108
|
+
textarea.style.height = 'auto'
|
|
109
|
+
|
|
110
|
+
// calculate content height
|
|
111
|
+
const contentHeight = textarea.scrollHeight
|
|
112
|
+
|
|
113
|
+
// auto adjust height between min and max height
|
|
114
|
+
let newHeight = Math.max(contentHeight, minHeight)
|
|
115
|
+
newHeight = Math.min(newHeight, maxHeight)
|
|
116
|
+
|
|
117
|
+
textarea.style.height = `${newHeight}px`
|
|
118
|
+
|
|
119
|
+
// if content height is greater than max height, show scrollbar
|
|
120
|
+
if (contentHeight > maxHeight) {
|
|
121
|
+
textarea.style.overflowY = 'auto'
|
|
122
|
+
} else {
|
|
123
|
+
textarea.style.overflowY = 'hidden'
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// if height increased and auto scroll is enabled, scroll to appropriate position
|
|
127
|
+
if (autoScroll && (newHeight > parseInt(oldHeight) || !oldHeight)) {
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
const rect = textarea.getBoundingClientRect()
|
|
130
|
+
window.scrollTo({
|
|
131
|
+
top: window.pageYOffset + rect.bottom + extraScrollSpace - window.innerHeight,
|
|
132
|
+
behavior: 'smooth'
|
|
133
|
+
})
|
|
134
|
+
}, 0)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// when value changes, adjust height
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
const timer = setTimeout(() => {
|
|
142
|
+
adjustTextareaHeight()
|
|
143
|
+
}, 0)
|
|
144
|
+
return () => clearTimeout(timer)
|
|
145
|
+
}, [value, minHeight, maxHeight, autoScroll, extraScrollSpace])
|
|
146
|
+
|
|
147
|
+
// handle input, limit max words
|
|
148
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
149
|
+
const inputValue = e.target.value
|
|
150
|
+
const words = inputValue.trim().split(/\s+/).filter(Boolean)
|
|
151
|
+
|
|
152
|
+
// if already reached max words, and this input will exceed limit, do not update
|
|
153
|
+
if (wordCount >= maxWords && words.length > maxWords) {
|
|
154
|
+
onWordLimitChange(true)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (words.length > maxWords) {
|
|
159
|
+
onChange(words.slice(0, maxWords).join(' '))
|
|
160
|
+
onWordLimitChange(true)
|
|
161
|
+
} else {
|
|
162
|
+
onChange(inputValue)
|
|
163
|
+
onWordLimitChange(false)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// when paste, also check word count
|
|
168
|
+
const handlePaste = (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
|
169
|
+
const paste = e.clipboardData.getData('text')
|
|
170
|
+
const currentWords = value.trim().split(/\s+/).filter(Boolean)
|
|
171
|
+
const pasteWords = paste.trim().split(/\s+/).filter(Boolean)
|
|
172
|
+
|
|
173
|
+
if (currentWords.length >= maxWords) {
|
|
174
|
+
e.preventDefault()
|
|
175
|
+
onWordLimitChange(true)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// only allow paste remaining words
|
|
180
|
+
const allowed = maxWords - currentWords.length
|
|
181
|
+
if (pasteWords.length > allowed) {
|
|
182
|
+
e.preventDefault()
|
|
183
|
+
const newWords = currentWords.concat(pasteWords.slice(0, allowed))
|
|
184
|
+
onChange(newWords.join(' '))
|
|
185
|
+
onWordLimitChange(true)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 渲染标题组件
|
|
190
|
+
const renderTitle = () => {
|
|
191
|
+
if (title?.trim()) return null
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<div className="space-y-1">
|
|
195
|
+
{title && <span className="text-xl font-semibold text-foreground">{title}</span>}
|
|
196
|
+
{description?.trim() && <span className="text-sm text-gray-400 ml-1">{description}</span>}
|
|
197
|
+
</div>
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 渲染textarea组件
|
|
202
|
+
const renderTextarea = (isEmbedded = false) => (
|
|
203
|
+
<textarea
|
|
204
|
+
ref={textareaRef}
|
|
205
|
+
value={value}
|
|
206
|
+
onChange={handleInputChange}
|
|
207
|
+
onPaste={handlePaste}
|
|
208
|
+
placeholder={placeholder}
|
|
209
|
+
disabled={disabled}
|
|
210
|
+
className={`w-full p-4 bg-transparent ${isEmbedded ? 'border-0' : 'border-2 border-border rounded-lg'} focus:outline-none focus:border-purple-400 hover:border-purple-500 transition-colors text-foreground placeholder-muted-foreground placeholder:text-base disabled:bg-muted disabled:cursor-not-allowed resize-none ${className}`}
|
|
211
|
+
style={{ minHeight: `${minHeight}px` }}
|
|
212
|
+
/>
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
// 渲染单词计数
|
|
216
|
+
const renderWordCount = () => {
|
|
217
|
+
if (!showWordCount) return null
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<div className="flex justify-end">
|
|
221
|
+
<span
|
|
222
|
+
className={`text-sm ${
|
|
223
|
+
wordCount >= maxWords ? 'text-red-500' : wordCount > maxWords * 0.75 ? 'text-orange-500' : 'text-muted-foreground'
|
|
224
|
+
} ${isWordLimit ? 'animate-bounce' : ''}`}
|
|
225
|
+
onAnimationEnd={() => onWordLimitChange(false)}
|
|
226
|
+
>
|
|
227
|
+
{wordCount}/{maxWords} {wordUnitTitle}
|
|
228
|
+
</span>
|
|
229
|
+
</div>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 如果有标题且需要嵌入,则渲染内部标题布局
|
|
234
|
+
if (embed && (title)) {
|
|
235
|
+
return (
|
|
236
|
+
<div className="space-y-2">
|
|
237
|
+
<div className="border-2 border-border rounded-lg bg-transparent">
|
|
238
|
+
<div className="p-4 pb-2">
|
|
239
|
+
{renderTitle()}
|
|
240
|
+
</div>
|
|
241
|
+
<hr className="border-t-1 border-border" />
|
|
242
|
+
<div className="p-1">
|
|
243
|
+
{renderTextarea(true)}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
{renderWordCount()}
|
|
247
|
+
</div>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// 默认布局:外部标题或无标题
|
|
252
|
+
return (
|
|
253
|
+
<div className="space-y-2">
|
|
254
|
+
{renderTitle()}
|
|
255
|
+
{renderTextarea()}
|
|
256
|
+
{renderWordCount()}
|
|
257
|
+
</div>
|
|
258
|
+
)
|
|
259
|
+
}
|
package/src/styles/fuma.css
CHANGED
|
@@ -1,47 +1,14 @@
|
|
|
1
|
+
|
|
1
2
|
/* Has Banner */
|
|
2
|
-
.has-banner .sticky.top-0.z-40 {
|
|
3
|
-
position: fixed !important;
|
|
4
|
-
top: 0 !important;
|
|
5
|
-
left: 0 !important;
|
|
6
|
-
width: 100vw !important;
|
|
7
|
-
z-index: 1001 !important;
|
|
8
|
-
height: 3rem !important;
|
|
9
|
-
min-height: 3rem !important;
|
|
10
|
-
max-height: 3rem !important;
|
|
11
|
-
margin: 0 !important;
|
|
12
|
-
border-radius: 0 !important;
|
|
13
|
-
}
|
|
14
3
|
.has-banner header#nd-nav {
|
|
15
4
|
top: 2.5rem !important;
|
|
16
5
|
}
|
|
17
6
|
|
|
18
7
|
/* No Banner */
|
|
19
|
-
.no-banner .sticky.top-0.z-40 {
|
|
20
|
-
position: fixed !important;
|
|
21
|
-
top: 0 !important;
|
|
22
|
-
left: 0 !important;
|
|
23
|
-
width: 100vw !important;
|
|
24
|
-
z-index: 1001 !important;
|
|
25
|
-
height: 0.5rem !important;
|
|
26
|
-
min-height: 0.5rem !important;
|
|
27
|
-
max-height: 0.5rem !important;
|
|
28
|
-
margin: 0 !important;
|
|
29
|
-
border-radius: 0 !important;
|
|
30
|
-
}
|
|
31
8
|
.no-banner header#nd-nav {
|
|
32
9
|
top: 0rem !important;
|
|
33
10
|
}
|
|
34
11
|
|
|
35
|
-
/* Banner */
|
|
36
|
-
.has-banner main,
|
|
37
|
-
.has-banner .main-content {
|
|
38
|
-
padding-top: 3rem;
|
|
39
|
-
}
|
|
40
|
-
.no-banner main,
|
|
41
|
-
.no-banner .main-content {
|
|
42
|
-
padding-top: 0.5rem;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
12
|
/* Custome Fuma Steps */
|
|
46
13
|
.fd-step::before {
|
|
47
14
|
@apply size-5 -start-2.5 rounded-full;
|
|
@@ -136,4 +103,4 @@
|
|
|
136
103
|
div[role="dialog"].rounded-lg.border.bg-fd-popover {
|
|
137
104
|
min-width: 150px !important;
|
|
138
105
|
/* max-width: 150px !important; */
|
|
139
|
-
}
|
|
106
|
+
}
|