Commit 95adc509 authored by Яков's avatar Яков
Browse files

выбор загруженных

parent 70a43a9a
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<value> <value>
<TabSessionState> <TabSessionState>
<option name="provider" value="claude" /> <option name="provider" value="claude" />
<option name="sessionId" value="bca3c9d2-5b93-479f-bd56-e8e8b8ddc1a3" /> <option name="sessionId" value="280f06de-8bd6-4fec-afa0-84fcb8360cf4" />
<option name="cwd" value="$PROJECT_DIR$" /> <option name="cwd" value="$PROJECT_DIR$" />
<option name="model" value="claude-sonnet-4-6" /> <option name="model" value="claude-sonnet-4-6" />
<option name="permissionMode" value="bypassPermissions" /> <option name="permissionMode" value="bypassPermissions" />
......
{ {
"name": "react-ag-qeditor", "name": "react-ag-qeditor",
"version": "1.1.43", "version": "1.1.44",
"description": "WYSIWYG html editor", "description": "WYSIWYG html editor",
"author": "atma", "author": "atma",
"license": "MIT", "license": "MIT",
......
...@@ -137,6 +137,8 @@ const QEditor = ({ ...@@ -137,6 +137,8 @@ const QEditor = ({
const [isUploading, setIsUploading] = useState(false) const [isUploading, setIsUploading] = useState(false)
const [recordType, setRecordType] = useState({video: true}) const [recordType, setRecordType] = useState({video: true})
const [buttonLinkData, setButtonLinkData] = useState(defaultButtonLinkData) const [buttonLinkData, setButtonLinkData] = useState(defaultButtonLinkData)
const [suggestedFiles, setSuggestedFiles] = useState([])
const [suggestedLoading, setSuggestedLoading] = useState(false)
let formRef = useRef(null); let formRef = useRef(null);
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
...@@ -176,6 +178,33 @@ const QEditor = ({ ...@@ -176,6 +178,33 @@ const QEditor = ({
} }
}, [focusFromTo]) }, [focusFromTo])
const SUGGEST_TYPES = ['image', 'interactiveImage', 'video', 'audio', 'file']
const SUGGEST_TYPE_MAP = {
image: 'image',
interactiveImage: 'image',
video: 'video',
audio: 'audio',
file: 'file'
}
useEffect(() => {
if (!modalIsOpen || !uploadOptions.suggestUrl || !SUGGEST_TYPES.includes(innerModalType)) {
setSuggestedFiles([])
return
}
let cancelled = false
setSuggestedLoading(true)
axios.get(uploadOptions.suggestUrl, { params: { type: SUGGEST_TYPE_MAP[innerModalType] } })
.then(response => {
if (!cancelled && response.data.state === 'success' && Array.isArray(response.data.files)) {
setSuggestedFiles(response.data.files)
}
})
.catch(() => {})
.finally(() => { if (!cancelled) setSuggestedLoading(false) })
return () => { cancelled = true }
}, [modalIsOpen, innerModalType])
const editWord = (values) => { const editWord = (values) => {
let _values = values; let _values = values;
...@@ -803,6 +832,63 @@ const QEditor = ({ ...@@ -803,6 +832,63 @@ const QEditor = ({
} }
} }
const getSuggestionsSection = () => {
if (!uploadOptions.suggestUrl || !SUGGEST_TYPES.includes(innerModalType)) {
return null
}
if (suggestedLoading) {
return <div className='atma-editor-suggest-loading'>Загрузка файлов...</div>
}
if (suggestedFiles.length === 0) {
return null
}
const isVideo = innerModalType === 'video'
const isAudio = innerModalType === 'audio'
const isFile = innerModalType === 'file'
return (
<div className='atma-editor-suggest'>
<div className='atma-editor-suggest-title'>Ранее загруженные файлы</div>
<div className='atma-editor-suggest-list'>
{suggestedFiles.map((file, i) => {
const uid = 'suggest_' + file.path
const isSelected = uploadedPaths.some(p => p.uid === uid)
const thumbnail = isVideo
? (file.poster || file.path + '.jpg')
: (!isAudio && !isFile ? file.path : null)
return (
<div
key={'suggest' + i}
className={
'atma-editor-suggest-item' +
(isSelected ? ' selected' : '') +
(isFile ? ' is-file' : '') +
(isAudio ? ' is-audio' : '') +
(isVideo ? ' is-video' : '')
}
style={thumbnail ? { backgroundImage: `url(${thumbnail})` } : {}}
title={file.name}
onClick={() => {
if (isSelected) {
setUploadedPaths(uploadedPaths.filter(p => p.uid !== uid))
} else {
setUploadedPaths([...uploadedPaths, {
path: file.path,
uid,
name: file.name,
size: file.size
}])
}
}}
>
<span className='atma-editor-suggest-item-name'>{file.name}</span>
</div>
)
})}
</div>
</div>
)
}
const getInnerModal = () => { const getInnerModal = () => {
switch (innerModalType) { switch (innerModalType) {
case 'iframe': case 'iframe':
...@@ -831,7 +917,10 @@ const QEditor = ({ ...@@ -831,7 +917,10 @@ const QEditor = ({
) )
case 'audio': case 'audio':
return ( return (
<Fragment>{getUploader({accept: '.wav, .mp3, .ogg'})}</Fragment> <Fragment>
{getSuggestionsSection()}
{getUploader({accept: '.wav, .mp3, .ogg'})}
</Fragment>
) )
case 'iframe_pdf': case 'iframe_pdf':
return ( return (
...@@ -852,14 +941,30 @@ const QEditor = ({ ...@@ -852,14 +941,30 @@ const QEditor = ({
</Fragment> </Fragment>
) )
case 'video': case 'video':
return <Fragment>{getUploader({accept: 'video/mp4,.mp4'})}</Fragment> return (
<Fragment>
{getSuggestionsSection()}
{getUploader({accept: 'video/mp4,.mp4'})}
</Fragment>
)
case 'image': case 'image':
return <Fragment>{getUploader({accept: 'image/*'})}</Fragment> return (
<Fragment>
{getSuggestionsSection()}
{getUploader({accept: 'image/*'})}
</Fragment>
)
case 'interactiveImage': case 'interactiveImage':
return <Fragment>{getUploader({accept: 'image/*', multiple: false})}</Fragment> return (
<Fragment>
{getSuggestionsSection()}
{getUploader({accept: 'image/*', multiple: false})}
</Fragment>
)
case 'file': case 'file':
return ( return (
<Fragment> <Fragment>
{getSuggestionsSection()}
{getUploader({accept: '*', afterParams: ['no_convert=1']})} {getUploader({accept: '*', afterParams: ['no_convert=1']})}
</Fragment> </Fragment>
) )
......
...@@ -830,6 +830,98 @@ body{ ...@@ -830,6 +830,98 @@ body{
} }
} }
&-suggest{
margin-bottom: 20px;
&-loading{
color: #656D77;
font-size: 13px;
margin-bottom: 16px;
}
&-title{
font-size: 13px;
color: #656D77;
margin-bottom: 10px;
}
&-list{
display: flex;
flex-wrap: wrap;
gap: 10px;
}
&-item{
position: relative;
width: 100px;
aspect-ratio: 16 / 9;
background-color: #e8eaed;
border-radius: 6px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
border: 2px solid transparent;
transition: border-color 0.15s;
overflow: hidden;
&:hover{
border-color: #adb0b6;
}
&.selected{
border-color: #1790FF;
&::after{
content: '';
position: absolute;
top: 4px;
right: 4px;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #1790FF;
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%228%22%20height%3D%226%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M1%203l2%202%204-4%22%20stroke%3D%22%23fff%22%20stroke-width%3D%221.5%22%20fill%3D%22none%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E');
background-repeat: no-repeat;
background-position: center;
}
}
&.is-audio,
&.is-file{
aspect-ratio: auto;
width: auto;
max-width: 160px;
min-width: 80px;
height: auto;
background-image: none !important;
background-color: #F5F7FA;
display: flex;
align-items: center;
padding: 8px 10px;
}
&.is-video{
background-color: #1a1a1a;
}
&-name{
font-size: 11px;
color: #434A53;
display: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}
&.is-audio &-name,
&.is-file &-name{
display: block;
}
}
}
&-btn{ &-btn{
font-size: 16px; font-size: 16px;
-webkit-appearance: none; -webkit-appearance: none;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment