Skip to content

Commit 460b304

Browse files
committed
fix(emcn): restore upload spinner via loading prop on ChipModalField file control
Addresses review feedback — the shared file drop zone now accepts an optional loading prop that renders an animated spinner and blocks further picks while an async import is in flight, restoring the feedback the skill import modal lost when it migrated off its bespoke drop zone.
1 parent e6ef14e commit 460b304

2 files changed

Lines changed: 18 additions & 5 deletions

File tree

apps/sim/app/workspace/[workspaceId]/skills/components/skill-import/skill-import.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export function SkillImport({ onImport }: SkillImportProps) {
128128
title='Upload File'
129129
accept='.md,.zip'
130130
onChange={handleFiles}
131-
disabled={fileState === 'loading'}
131+
loading={fileState === 'loading'}
132132
label={fileState === 'loading' ? 'Importing…' : undefined}
133133
description='.md file with YAML frontmatter, or .zip containing a SKILL.md'
134134
error={fileError || undefined}

apps/sim/components/emcn/components/chip-modal/chip-modal.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import { ChipTextarea } from '@/components/emcn/components/chip-textarea/chip-te
5252
import { Label } from '@/components/emcn/components/label/label'
5353
import { Modal, ModalContent } from '@/components/emcn/components/modal/modal'
5454
import { TagInput, type TagItem } from '@/components/emcn/components/tag-input/tag-input'
55+
import { Loader } from '@/components/emcn/icons'
5556
import { cn } from '@/lib/core/utils/cn'
5657
import { quickValidateEmail } from '@/lib/messaging/email/validation'
5758

@@ -377,6 +378,14 @@ interface ChipModalFileFieldProps extends ChipModalFieldBaseProps {
377378
* for a single-line zone.
378379
*/
379380
description?: React.ReactNode
381+
/**
382+
* Renders a spinner inside the drop zone and blocks further picks while an
383+
* async import/upload is in flight. Use for slow selections (zip extraction,
384+
* remote fetches) where the zone would otherwise look idle. Pair with a
385+
* `label` such as `'Importing…'` for an explicit status line.
386+
* @default false
387+
*/
388+
loading?: boolean
380389
}
381390

382391
export interface ChipModalEmailsFieldProps extends ChipModalFieldBaseProps {
@@ -692,6 +701,7 @@ function ChipModalFileControl({
692701
multiple = false,
693702
label = 'Drop files here or click to browse',
694703
description,
704+
loading = false,
695705
disabled,
696706
id,
697707
'aria-required': ariaRequired,
@@ -700,6 +710,7 @@ function ChipModalFileControl({
700710
}: ChipModalFileFieldProps & { id: string } & React.AriaAttributes) {
701711
const inputRef = React.useRef<HTMLInputElement>(null)
702712
const [isDragging, setIsDragging] = React.useState(false)
713+
const isInteractive = !disabled && !loading
703714

704715
const emitFiles = React.useCallback(
705716
(files: FileList | null) => {
@@ -713,15 +724,16 @@ function ChipModalFileControl({
713724
<button
714725
type='button'
715726
id={id}
716-
disabled={disabled}
727+
disabled={!isInteractive}
728+
aria-busy={loading || undefined}
717729
aria-required={ariaRequired}
718730
aria-invalid={ariaInvalid}
719731
aria-describedby={ariaDescribedby}
720732
onClick={() => inputRef.current?.click()}
721733
onDragEnter={(event) => {
722734
event.preventDefault()
723735
event.stopPropagation()
724-
if (!disabled) setIsDragging(true)
736+
if (isInteractive) setIsDragging(true)
725737
}}
726738
onDragOver={(event) => {
727739
event.preventDefault()
@@ -736,7 +748,7 @@ function ChipModalFileControl({
736748
event.preventDefault()
737749
event.stopPropagation()
738750
setIsDragging(false)
739-
if (!disabled) emitFiles(event.dataTransfer.files)
751+
if (isInteractive) emitFiles(event.dataTransfer.files)
740752
}}
741753
className={cn(
742754
'flex w-full flex-col items-center justify-center gap-0.5 rounded-lg border border-[var(--border-1)] border-dashed bg-[var(--surface-5)] px-2 py-2.5 text-center outline-none transition-colors hover-hover:border-[var(--surface-7)] disabled:cursor-not-allowed disabled:opacity-50 dark:bg-[var(--surface-4)]',
@@ -748,13 +760,14 @@ function ChipModalFileControl({
748760
type='file'
749761
accept={accept}
750762
multiple={multiple}
751-
disabled={disabled}
763+
disabled={!isInteractive}
752764
className='hidden'
753765
onChange={(event) => {
754766
emitFiles(event.target.files)
755767
event.target.value = ''
756768
}}
757769
/>
770+
{loading ? <Loader animate className='size-[14px] text-[var(--text-tertiary)]' /> : null}
758771
<span className='text-[var(--text-primary)] text-caption'>
759772
{isDragging ? 'Drop files here' : label}
760773
</span>

0 commit comments

Comments
 (0)