mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
clean up
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
+22
-22
File diff suppressed because one or more lines are too long
@@ -31,6 +31,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
& .fi-fo-rich-editor-file-validation-message {
|
||||
@apply border-danger-200 bg-danger-50 text-danger-700 dark:text-danger-200 flex items-center gap-x-3 border-b px-5 py-1.5 text-sm leading-6 font-medium dark:border-white/10 dark:bg-white/5;
|
||||
}
|
||||
|
||||
& .fi-fo-rich-editor-main {
|
||||
@apply flex flex-col-reverse;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export default function fileUploadFormComponent({
|
||||
itemInsertLocation: shouldAppendFiles ? 'after' : 'before',
|
||||
...(placeholder && { labelIdle: placeholder }),
|
||||
maxFiles,
|
||||
maxFileSize: maxSize,
|
||||
fileAttachmentsMaxFileSize: maxSize,
|
||||
minFileSize: minSize,
|
||||
...(maxParallelUploads && { maxParallelUploads }),
|
||||
styleButtonProcessItemPosition: uploadButtonPosition,
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Selection } from '@tiptap/pm/state'
|
||||
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'
|
||||
|
||||
export default function richEditorFormComponent({
|
||||
acceptedFileTypes,
|
||||
acceptedFileTypesValidationMessage,
|
||||
activePanel,
|
||||
deleteCustomBlockButtonIconHtml,
|
||||
editCustomBlockButtonIconHtml,
|
||||
@@ -14,16 +16,14 @@ export default function richEditorFormComponent({
|
||||
isLiveOnBlur,
|
||||
liveDebounce,
|
||||
livewireId,
|
||||
maxFileSize,
|
||||
maxFileSizeValidationMessage,
|
||||
mergeTags,
|
||||
noMergeTagSearchResultsMessage,
|
||||
placeholder,
|
||||
state,
|
||||
statePath,
|
||||
uploadingFileMessage,
|
||||
allowedMimeTypes,
|
||||
maxFileSize,
|
||||
fileSizeExceededMessage,
|
||||
invalidMimeTypeMessage,
|
||||
floatingToolbars,
|
||||
}) {
|
||||
let editor
|
||||
@@ -37,6 +37,8 @@ export default function richEditorFormComponent({
|
||||
|
||||
isUploadingFile: false,
|
||||
|
||||
fileValidationMessage: null,
|
||||
|
||||
shouldUpdateState: true,
|
||||
|
||||
editorUpdatedAt: Date.now(),
|
||||
@@ -46,6 +48,8 @@ export default function richEditorFormComponent({
|
||||
editable: !isDisabled,
|
||||
element: this.$refs.editor,
|
||||
extensions: await getExtensions({
|
||||
acceptedFileTypes,
|
||||
acceptedFileTypesValidationMessage,
|
||||
customExtensionUrls: extensions,
|
||||
deleteCustomBlockButtonIconHtml,
|
||||
editCustomBlockButtonIconHtml,
|
||||
@@ -67,15 +71,13 @@ export default function richEditorFormComponent({
|
||||
{ schemaComponent: key },
|
||||
),
|
||||
key,
|
||||
maxFileSize,
|
||||
maxFileSizeValidationMessage,
|
||||
mergeTags,
|
||||
noMergeTagSearchResultsMessage,
|
||||
placeholder,
|
||||
statePath,
|
||||
uploadingFileMessage,
|
||||
allowedMimeTypes,
|
||||
maxFileSize,
|
||||
fileSizeExceededMessage,
|
||||
invalidMimeTypeMessage,
|
||||
$wire: this.$wire,
|
||||
floatingToolbars,
|
||||
}),
|
||||
@@ -123,6 +125,8 @@ export default function richEditorFormComponent({
|
||||
|
||||
this.shouldUpdateState = false
|
||||
|
||||
this.fileValidationMessage = null
|
||||
|
||||
if (isLiveDebounced) {
|
||||
debouncedCommit()
|
||||
}
|
||||
@@ -170,6 +174,7 @@ export default function richEditorFormComponent({
|
||||
}
|
||||
|
||||
this.isUploadingFile = true
|
||||
this.fileValidationMessage = null
|
||||
|
||||
event.stopPropagation()
|
||||
})
|
||||
@@ -188,6 +193,24 @@ export default function richEditorFormComponent({
|
||||
event.stopPropagation()
|
||||
})
|
||||
|
||||
window.addEventListener(
|
||||
'rich-editor-file-validation-message',
|
||||
(event) => {
|
||||
if (event.detail.livewireId !== livewireId) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.detail.key !== key) {
|
||||
return
|
||||
}
|
||||
|
||||
this.isUploadingFile = false
|
||||
this.fileValidationMessage = event.detail.validationMessage
|
||||
|
||||
event.stopPropagation()
|
||||
},
|
||||
)
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(`schema-component-${livewireId}-${key}-loaded`),
|
||||
)
|
||||
|
||||
@@ -1,39 +1,6 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
const defaultAllowedMimeTypes = [
|
||||
'image/png',
|
||||
'image/jpeg',
|
||||
'image/gif',
|
||||
'image/webp',
|
||||
]
|
||||
const defaultMaxFileSize = 10 * 1024 * 1024 // 10MB in bytes
|
||||
const defaultFileSizeExceededMessage = 'File size exceeds maximum allowed'
|
||||
const defaultInvalidMimeTypeMessage = 'File type is not allowed'
|
||||
|
||||
const validateFile = (
|
||||
file,
|
||||
allowedMimeTypes,
|
||||
maxFileSize,
|
||||
invalidMimeTypeMessage,
|
||||
fileSizeExceededMessage,
|
||||
) => {
|
||||
const errors = []
|
||||
|
||||
if (!allowedMimeTypes.includes(file.type)) {
|
||||
errors.push(invalidMimeTypeMessage || defaultInvalidMimeTypeMessage)
|
||||
}
|
||||
|
||||
if (file.size > +maxFileSize * 1024) {
|
||||
errors.push(fileSizeExceededMessage || defaultFileSizeExceededMessage)
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
const dispatchFormEvent = (editorView, name, detail = {}) => {
|
||||
editorView.dom.closest('form')?.dispatchEvent(
|
||||
new CustomEvent(name, {
|
||||
@@ -44,21 +11,37 @@ const dispatchFormEvent = (editorView, name, detail = {}) => {
|
||||
)
|
||||
}
|
||||
|
||||
const validateFiles = ({
|
||||
files,
|
||||
acceptedTypes,
|
||||
acceptedTypesValidationMessage,
|
||||
maxSize,
|
||||
maxSizeValidationMessage,
|
||||
}) => {
|
||||
for (const file of files) {
|
||||
if (!acceptedTypes.includes(file.type)) {
|
||||
return acceptedTypesValidationMessage
|
||||
}
|
||||
|
||||
if (file.size > +maxSize * 1024) {
|
||||
return maxSizeValidationMessage
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const LocalFilesPlugin = ({
|
||||
editor,
|
||||
acceptedTypes,
|
||||
acceptedTypesValidationMessage,
|
||||
get$WireUsing,
|
||||
key,
|
||||
maxSize,
|
||||
maxSizeValidationMessage,
|
||||
statePath,
|
||||
uploadingMessage,
|
||||
allowedMimeTypes,
|
||||
maxFileSize,
|
||||
fileSizeExceededMessage,
|
||||
invalidMimeTypeMessage,
|
||||
}) => {
|
||||
// Use defaults if not provided from PHP
|
||||
const effectiveAllowedMimeTypes =
|
||||
allowedMimeTypes || defaultAllowedMimeTypes
|
||||
const effectiveMaxFileSize = maxFileSize || defaultMaxFileSize
|
||||
const getFileAttachmentUrl = (fileKey) =>
|
||||
get$WireUsing().callSchemaComponentMethod(
|
||||
key,
|
||||
@@ -76,44 +59,34 @@ const LocalFilesPlugin = ({
|
||||
return false
|
||||
}
|
||||
|
||||
const allFiles = Array.from(event.dataTransfer.files)
|
||||
const validFiles = []
|
||||
const rejectedFiles = []
|
||||
const files = Array.from(event.dataTransfer.files)
|
||||
|
||||
allFiles.forEach((file) => {
|
||||
const validation = validateFile(
|
||||
file,
|
||||
effectiveAllowedMimeTypes,
|
||||
effectiveMaxFileSize,
|
||||
invalidMimeTypeMessage,
|
||||
fileSizeExceededMessage,
|
||||
)
|
||||
if (validation.isValid) {
|
||||
validFiles.push(file)
|
||||
} else {
|
||||
rejectedFiles.push({ file, errors: validation.errors })
|
||||
}
|
||||
const validationMessage = validateFiles({
|
||||
files,
|
||||
acceptedTypes,
|
||||
acceptedTypesValidationMessage,
|
||||
maxSize,
|
||||
maxSizeValidationMessage,
|
||||
})
|
||||
|
||||
if (rejectedFiles.length > 0) {
|
||||
const errorMessages = rejectedFiles
|
||||
.map(
|
||||
({ file, errors }) =>
|
||||
`<p>${file.name}: ${errors.join(', ')}</p>`,
|
||||
)
|
||||
.join('')
|
||||
if (validationMessage) {
|
||||
editorView.dom.dispatchEvent(
|
||||
new CustomEvent('rich-editor-file-validation-message', {
|
||||
bubbles: true,
|
||||
detail: {
|
||||
key,
|
||||
livewireId: get$WireUsing().id,
|
||||
validationMessage,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
new FilamentNotification()
|
||||
.body(errorMessages)
|
||||
.danger()
|
||||
.send()
|
||||
}
|
||||
|
||||
if (!validFiles.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
const files = validFiles
|
||||
if (!files.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
dispatchFormEvent(editorView, 'form-processing-started', {
|
||||
message: uploadingMessage,
|
||||
@@ -139,22 +112,6 @@ const LocalFilesPlugin = ({
|
||||
}),
|
||||
)
|
||||
|
||||
const fileReader = new FileReader()
|
||||
|
||||
fileReader.readAsDataURL(file)
|
||||
fileReader.onload = () => {
|
||||
editor
|
||||
.chain()
|
||||
.insertContentAt(position?.pos ?? 0, {
|
||||
type: 'image',
|
||||
attrs: {
|
||||
class: 'fi-loading',
|
||||
src: fileReader.result,
|
||||
},
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
let fileKey = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(
|
||||
/[018]/g,
|
||||
(c) =>
|
||||
@@ -176,10 +133,12 @@ const LocalFilesPlugin = ({
|
||||
|
||||
editor
|
||||
.chain()
|
||||
.updateAttributes('image', {
|
||||
class: null,
|
||||
id: fileKey,
|
||||
src: url,
|
||||
.insertContentAt(position?.pos ?? 0, {
|
||||
type: 'image',
|
||||
attrs: {
|
||||
id: fileKey,
|
||||
src: url,
|
||||
},
|
||||
})
|
||||
.run()
|
||||
|
||||
@@ -219,44 +178,34 @@ const LocalFilesPlugin = ({
|
||||
return false
|
||||
}
|
||||
|
||||
const allFiles = Array.from(event.clipboardData.files)
|
||||
const validFiles = []
|
||||
const rejectedFiles = []
|
||||
const files = Array.from(event.clipboardData.files)
|
||||
|
||||
allFiles.forEach((file) => {
|
||||
const validation = validateFile(
|
||||
file,
|
||||
effectiveAllowedMimeTypes,
|
||||
effectiveMaxFileSize,
|
||||
invalidMimeTypeMessage,
|
||||
fileSizeExceededMessage,
|
||||
)
|
||||
if (validation.isValid) {
|
||||
validFiles.push(file)
|
||||
} else {
|
||||
rejectedFiles.push({ file, errors: validation.errors })
|
||||
}
|
||||
const validationMessage = validateFiles({
|
||||
files,
|
||||
acceptedTypes,
|
||||
acceptedTypesValidationMessage,
|
||||
maxSize,
|
||||
maxSizeValidationMessage,
|
||||
})
|
||||
|
||||
if (rejectedFiles.length > 0) {
|
||||
const errorMessages = rejectedFiles
|
||||
.map(
|
||||
({ file, errors }) =>
|
||||
`<p>${file.name}: ${errors.join(', ')}</p>`,
|
||||
)
|
||||
.join('')
|
||||
if (validationMessage) {
|
||||
editorView.dom.dispatchEvent(
|
||||
new CustomEvent('rich-editor-file-validation-message', {
|
||||
bubbles: true,
|
||||
detail: {
|
||||
key,
|
||||
livewireId: get$WireUsing().id,
|
||||
validationMessage,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
new FilamentNotification()
|
||||
.body(errorMessages)
|
||||
.danger()
|
||||
.send()
|
||||
}
|
||||
|
||||
if (!validFiles.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
const files = validFiles
|
||||
if (!files.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@@ -277,22 +226,6 @@ const LocalFilesPlugin = ({
|
||||
}),
|
||||
)
|
||||
|
||||
const fileReader = new FileReader()
|
||||
|
||||
fileReader.readAsDataURL(file)
|
||||
fileReader.onload = () => {
|
||||
editor
|
||||
.chain()
|
||||
.insertContentAt(editor.state.selection.anchor, {
|
||||
type: 'image',
|
||||
attrs: {
|
||||
class: 'fi-loading',
|
||||
src: fileReader.result,
|
||||
},
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
let fileKey = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(
|
||||
/[018]/g,
|
||||
(c) =>
|
||||
@@ -314,11 +247,16 @@ const LocalFilesPlugin = ({
|
||||
|
||||
editor
|
||||
.chain()
|
||||
.updateAttributes('image', {
|
||||
class: null,
|
||||
id: fileKey,
|
||||
src: url,
|
||||
})
|
||||
.insertContentAt(
|
||||
editor.state.selection.anchor,
|
||||
{
|
||||
type: 'image',
|
||||
attrs: {
|
||||
id: fileKey,
|
||||
src: url,
|
||||
},
|
||||
},
|
||||
)
|
||||
.run()
|
||||
|
||||
editor.setEditable(true)
|
||||
@@ -357,14 +295,14 @@ export default Extension.create({
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
acceptedTypes: [],
|
||||
acceptedTypesValidationMessage: null,
|
||||
key: null,
|
||||
maxSize: null,
|
||||
maxSizeValidationMessage: null,
|
||||
statePath: null,
|
||||
uploadingMessage: null,
|
||||
get$WireUsing: null,
|
||||
allowedMimeTypes: defaultAllowedMimeTypes,
|
||||
maxFileSize: defaultMaxFileSize,
|
||||
fileSizeExceededMessage: defaultFileSizeExceededMessage,
|
||||
invalidMimeTypeMessage: defaultInvalidMimeTypeMessage,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -35,21 +35,21 @@ import Underline from '@tiptap/extension-underline'
|
||||
import getMergeTagSuggestion from './merge-tag-suggestion.js'
|
||||
|
||||
export default async ({
|
||||
acceptedFileTypes,
|
||||
acceptedFileTypesValidationMessage,
|
||||
customExtensionUrls,
|
||||
deleteCustomBlockButtonIconHtml,
|
||||
editCustomBlockButtonIconHtml,
|
||||
editCustomBlockUsing,
|
||||
insertCustomBlockUsing,
|
||||
key,
|
||||
maxFileSize,
|
||||
maxFileSizeValidationMessage,
|
||||
mergeTags,
|
||||
noMergeTagSearchResultsMessage,
|
||||
placeholder,
|
||||
statePath,
|
||||
uploadingFileMessage,
|
||||
allowedMimeTypes,
|
||||
maxFileSize,
|
||||
fileSizeExceededMessage,
|
||||
invalidMimeTypeMessage,
|
||||
$wire,
|
||||
}) => [
|
||||
Blockquote,
|
||||
@@ -84,14 +84,14 @@ export default async ({
|
||||
}),
|
||||
ListItem,
|
||||
LocalFiles.configure({
|
||||
acceptedTypes: acceptedFileTypes,
|
||||
acceptedTypesValidationMessage: acceptedFileTypesValidationMessage,
|
||||
get$WireUsing: () => $wire,
|
||||
key,
|
||||
maxSize: maxFileSize,
|
||||
maxSizeValidationMessage: maxFileSizeValidationMessage,
|
||||
statePath,
|
||||
uploadingMessage: uploadingFileMessage,
|
||||
allowedMimeTypes,
|
||||
maxFileSize,
|
||||
fileSizeExceededMessage,
|
||||
invalidMimeTypeMessage,
|
||||
}),
|
||||
...(mergeTags.length
|
||||
? [
|
||||
|
||||
@@ -469,6 +469,10 @@ return [
|
||||
|
||||
],
|
||||
|
||||
'file_attachments_accepted_file_types_message' => 'Uploaded files must be of type: :values.',
|
||||
|
||||
'file_attachments_max_size_message' => 'Uploaded files must not be greater than :max kilobytes.',
|
||||
|
||||
'no_merge_tag_search_results_message' => 'No merge tag results.',
|
||||
|
||||
'tools' => [
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
$tools = $getTools();
|
||||
$toolbarButtons = $getToolbarButtons();
|
||||
$floatingToolbars = $getFloatingToolbars();
|
||||
$fileAttachmentsMaxSize = $getFileAttachmentsMaxSize();
|
||||
$fileAttachmentsAcceptedFileTypes = $getFileAttachmentsAcceptedFileTypes();
|
||||
@endphp
|
||||
|
||||
<x-dynamic-component :component="$fieldWrapperView" :field="$field">
|
||||
@@ -26,6 +28,8 @@
|
||||
x-load
|
||||
x-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('rich-editor', 'filament/forms') }}"
|
||||
x-data="richEditorFormComponent({
|
||||
acceptedFileTypes: @js($fileAttachmentsAcceptedFileTypes),
|
||||
acceptedFileTypesValidationMessage: @js($fileAttachmentsAcceptedFileTypes ? __('filament-forms::components.rich_editor.file_attachments_accepted_file_types_message', ['values' => implode(', ', $fileAttachmentsAcceptedFileTypes)]) : null),
|
||||
activePanel: @js($getActivePanel()),
|
||||
deleteCustomBlockButtonIconHtml: @js(\Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::Trash, alias: \Filament\Forms\View\FormsIconAlias::COMPONENTS_RICH_EDITOR_PANELS_CUSTOM_BLOCK_DELETE_BUTTON)->toHtml()),
|
||||
editCustomBlockButtonIconHtml: @js(\Filament\Support\generate_icon_html(\Filament\Support\Icons\Heroicon::PencilSquare, alias: \Filament\Forms\View\FormsIconAlias::COMPONENTS_RICH_EDITOR_PANELS_CUSTOM_BLOCK_EDIT_BUTTON)->toHtml()),
|
||||
@@ -36,16 +40,14 @@
|
||||
isLiveOnBlur: @js($isLiveOnBlur()),
|
||||
liveDebounce: @js($getNormalizedLiveDebounce()),
|
||||
livewireId: @js($this->getId()),
|
||||
maxFileSize: @js($fileAttachmentsMaxSize),
|
||||
maxFileSizeValidationMessage: @js($fileAttachmentsMaxSize ? trans_choice('filament-forms::components.rich_editor.file_attachments_max_size_message', $fileAttachmentsMaxSize, ['max' => $fileAttachmentsMaxSize]) : null),
|
||||
mergeTags: @js($mergeTags),
|
||||
noMergeTagSearchResultsMessage: @js($getNoMergeTagSearchResultsMessage()),
|
||||
placeholder: @js($getPlaceholder()),
|
||||
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')", isOptimisticallyLive: false) }},
|
||||
statePath: @js($statePath),
|
||||
uploadingFileMessage: @js($getUploadingFileMessage()),
|
||||
allowedMimeTypes: @js($getAcceptedFileTypes()),
|
||||
maxFileSize: @js($getMaxSize()),
|
||||
fileSizeExceededMessage: @js(__('validation.max.file', ['attribute' => '', 'max' => $getMaxSize()])),
|
||||
invalidMimeTypeMessage: @js(__('validation.mimetypes', ['attribute' => '', 'values' => $getAcceptedFileTypes() ? implode(', ', $getAcceptedFileTypes()) : ''])),
|
||||
floatingToolbars: @js($floatingToolbars),
|
||||
})"
|
||||
x-bind:class="{
|
||||
@@ -82,6 +84,17 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-show="! isUploadingFile && fileValidationMessage"
|
||||
x-cloak
|
||||
class="fi-fo-rich-editor-file-validation-message"
|
||||
>
|
||||
<span
|
||||
x-text="fileValidationMessage"
|
||||
x-show="! isUploadingFile && fileValidationMessage"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
{{ $getExtraInputAttributeBag()->class(['fi-fo-rich-editor-main']) }}
|
||||
>
|
||||
|
||||
@@ -5,8 +5,11 @@ namespace Filament\Forms\Components\Concerns;
|
||||
use Closure;
|
||||
use Filament\Support\Components\Attributes\ExposedLivewireMethod;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use League\Flysystem\UnableToCheckFileExistence;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
use Throwable;
|
||||
@@ -23,6 +26,13 @@ trait HasFileAttachments
|
||||
|
||||
protected string | Closure | null $fileAttachmentsVisibility = null;
|
||||
|
||||
/**
|
||||
* @var array<string> | Arrayable | Closure | null
|
||||
*/
|
||||
protected array | Arrayable | Closure | null $fileAttachmentsAcceptedFileTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
||||
|
||||
protected int | Closure | null $fileAttachmentsMaxSize = null;
|
||||
|
||||
public function fileAttachmentsDirectory(string | Closure | null $directory): static
|
||||
{
|
||||
$this->fileAttachmentsDirectory = $directory;
|
||||
@@ -51,6 +61,26 @@ trait HasFileAttachments
|
||||
$attachment = data_get($this->getLivewire(), "componentFileAttachments.{$this->getStatePath()}");
|
||||
}
|
||||
|
||||
if ($attachment instanceof TemporaryUploadedFile) {
|
||||
$maxSize = $this->getFileAttachmentsMaxSize();
|
||||
$acceptedFileTypes = $this->getFileAttachmentsAcceptedFileTypes();
|
||||
|
||||
try {
|
||||
Validator::validate(
|
||||
['file' => $attachment],
|
||||
rules: [
|
||||
'file' => [
|
||||
'file',
|
||||
...($maxSize ? ["max:{$maxSize}"] : []),
|
||||
...($acceptedFileTypes ? ['mimetypes:' . implode(',', $acceptedFileTypes)] : []),
|
||||
],
|
||||
],
|
||||
);
|
||||
} catch (ValidationException $exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
@@ -218,4 +248,40 @@ trait HasFileAttachments
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> | Arrayable | Closure $types
|
||||
*/
|
||||
public function fileAttachmentsAcceptedFileTypes(array | Arrayable | Closure $types): static
|
||||
{
|
||||
$this->fileAttachmentsAcceptedFileTypes = $types;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string> | null
|
||||
*/
|
||||
public function getFileAttachmentsAcceptedFileTypes(): ?array
|
||||
{
|
||||
$types = $this->evaluate($this->fileAttachmentsAcceptedFileTypes);
|
||||
|
||||
if ($types instanceof Arrayable) {
|
||||
$types = $types->toArray();
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function fileAttachmentsMaxSize(int | Closure | null $size): static
|
||||
{
|
||||
$this->fileAttachmentsMaxSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFileAttachmentsMaxSize(): ?int
|
||||
{
|
||||
return $this->evaluate($this->fileAttachmentsMaxSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ use Filament\Forms\Components\RichEditor\StateCasts\RichEditorStateCast;
|
||||
use Filament\Schemas\Components\StateCasts\Contracts\StateCast;
|
||||
use Filament\Support\Concerns\HasExtraAlpineAttributes;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -83,13 +82,6 @@ class RichEditor extends Field implements Contracts\CanBeLengthConstrained
|
||||
*/
|
||||
protected array | Closure | null $floatingToolbars = null;
|
||||
|
||||
/**
|
||||
* @var array<string> | Arrayable | Closure | null
|
||||
*/
|
||||
protected array | Arrayable | Closure | null $acceptedFileTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp'];
|
||||
|
||||
protected int | Closure | null $maxSize = null;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@@ -936,40 +928,4 @@ class RichEditor extends Field implements Contracts\CanBeLengthConstrained
|
||||
|
||||
return parent::callAfterStateUpdated($shouldBubbleToParents);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string> | Arrayable | Closure $types
|
||||
*/
|
||||
public function acceptedFileTypes(array | Arrayable | Closure $types): static
|
||||
{
|
||||
$this->acceptedFileTypes = $types;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string> | null
|
||||
*/
|
||||
public function getAcceptedFileTypes(): ?array
|
||||
{
|
||||
$types = $this->evaluate($this->acceptedFileTypes);
|
||||
|
||||
if ($types instanceof Arrayable) {
|
||||
$types = $types->toArray();
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function maxSize(int | Closure | null $size): static
|
||||
{
|
||||
$this->maxSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxSize(): ?int
|
||||
{
|
||||
return $this->evaluate($this->maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ class AttachFilesAction
|
||||
->label(filled($arguments['src'] ?? null)
|
||||
? __('filament-forms::components.rich_editor.actions.attach_files.modal.form.file.label.existing')
|
||||
: __('filament-forms::components.rich_editor.actions.attach_files.modal.form.file.label.new'))
|
||||
->acceptedFileTypes(fn () => $component->getAcceptedFileTypes())
|
||||
->maxSize(fn () => $component->getMaxSize())
|
||||
->acceptedFileTypes($component->getFileAttachmentsAcceptedFileTypes())
|
||||
->maxSize($component->getFileAttachmentsMaxSize())
|
||||
->storeFiles(false)
|
||||
->required(blank($arguments['src'] ?? null))
|
||||
->hiddenLabel(blank($arguments['src'] ?? null)),
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user