UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

143 lines (142 loc) 7.52 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; /* * Copyright 2025 The Kubernetes Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Icon, InlineIcon } from '@iconify/react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogContent from '@mui/material/DialogContent'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { DropZoneBox } from '../DropZoneBox'; import Tabs from '../Tabs'; const ActionButtons = ({ onBack, onLoad, disabled, }) => { const { t } = useTranslation(); return (_jsxs(Box, { sx: { display: 'flex', mt: 2, justifyContent: 'space-between' }, children: [_jsxs(Button, { onClick: onBack, color: "inherit", variant: "outlined", children: [_jsx(Icon, { icon: "mdi:arrow-left", width: "18", height: "18", style: { display: 'inline-block', marginRight: '8px' } }), t('translation|Back')] }), _jsx(Button, { onClick: onLoad, color: "inherit", variant: "contained", disabled: disabled, children: t('translation|Load') })] })); }; const UploadFromFilesystem = ({ onLoaded, onCancel, }) => { const { t } = useTranslation(); const [files, setFiles] = React.useState([]); const [dragOver, setDragOver] = React.useState(false); const [error, setError] = React.useState(''); const fileInputRef = React.useRef(null); const onFilesPicked = (picked) => { setError(''); setFiles(picked ? Array.from(picked) : []); }; const readFileAsText = (file) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => resolve(String(e.target?.result ?? '')); reader.onerror = () => reject(new Error(t('translation|Failed to read file.'))); reader.onabort = () => reject(new Error(t('translation|File read was aborted.'))); reader.readAsText(file); }); const handleLoadFiles = async () => { try { setError(''); if (files.every(f => f.size === 0)) { setError(t('translation|Error: All of the files are empty.')); } const texts = await Promise.all(files.map(readFileAsText)); const merged = texts.join('---\n'); onLoaded(merged); } catch (e) { setError(e.message || t('translation|Unexpected error while reading files.')); } }; const handleDrop = (e) => { e.preventDefault(); setDragOver(false); onFilesPicked(e.dataTransfer.files); }; const handleDragOver = (e) => { e.preventDefault(); setDragOver(true); }; const handleDragLeave = () => setDragOver(false); return (_jsxs(_Fragment, { children: [_jsxs(DropZoneBox, { onDrop: handleDrop, onDragOver: handleDragOver, onDragLeave: handleDragLeave, children: [_jsx(Typography, { sx: { m: 2 }, children: dragOver ? t('translation|Drop the file here...') : t('translation|Select a file or drag and drop here') }), _jsx("input", { ref: fileInputRef, type: "file", accept: ".yaml,.yml,application/yaml", multiple: true, hidden: true, onChange: e => onFilesPicked(e.target.files) }), _jsx(Button, { variant: "contained", onClick: () => fileInputRef.current?.click(), startIcon: _jsx(InlineIcon, { icon: "mdi:upload", width: 32 }), sx: { fontWeight: 500 }, children: t('translation|Select File') })] }), !!files.length && (_jsxs(Box, { sx: { borderRadius: 1, mt: 2, p: 1, width: '100%', border: '1px', fontWeight: 'bold', }, children: [files.length === 1 ? files[0].name : t('translation|{{count}} files selected', { count: files.length }), ' ', files.length > 1 && (_jsx(Typography, { variant: "body2", component: "div", sx: { mt: 1 }, children: files.map(f => f.name).join(', ') }))] })), error && (_jsx(Typography, { sx: { mt: 1 }, color: "error", children: error })), _jsx(ActionButtons, { onBack: onCancel, onLoad: handleLoadFiles, disabled: !files.length })] })); }; const UploadFromUrl = ({ onLoaded, onCancel, }) => { const { t } = useTranslation(); const [url, setUrl] = React.useState(''); const [error, setError] = React.useState(''); const parseUrl = (raw) => { try { const u = new URL(raw.trim()); return u.protocol === 'http:' || u.protocol === 'https:' ? u : null; } catch { return null; } }; const loadFromUrl = async () => { setError(''); const u = parseUrl(url); if (!u) { setError(t('translation|Please enter a valid URL.')); return; } try { const res = await fetch(u.toString()); if (!res.ok) { setError(t(`translation|Failed to fetch file: ${res.statusText}`)); } const text = await res.text(); onLoaded(text); } catch (e) { setError(e.message || t('translation|Unexpected error while fetching the file.')); } }; return (_jsxs(_Fragment, { children: [_jsx(Box, { sx: { pt: 1 }, children: _jsx(TextField, { label: t('translation|Enter URL'), variant: "outlined", fullWidth: true, value: url, onChange: e => { setUrl(e.target.value); setError(''); }, sx: { mb: 2 }, error: !!error }) }), error && (_jsx(Typography, { sx: { mt: 1 }, color: "error", children: error })), _jsx(ActionButtons, { onBack: onCancel, onLoad: loadFromUrl, disabled: !url })] })); }; export function UploadDialog(props) { const { setUploadFiles, setCode } = props; const { t } = useTranslation(); const finishLoad = (text) => { setCode({ format: 'yaml', code: text }); setUploadFiles(false); }; return (_jsx(Dialog, { open: true, onClose: () => setUploadFiles(false), fullWidth: true, slotProps: { backdrop: { sx: { backdropFilter: 'blur(2px)' } }, }, children: _jsx(DialogContent, { sx: { pt: 1 }, children: _jsx(Tabs, { ariaLabel: t('translation|Upload File/URL'), tabs: [ { label: t('translation|Upload File'), component: (_jsx(Box, { pt: 2, children: _jsx(UploadFromFilesystem, { onLoaded: finishLoad, onCancel: () => setUploadFiles(false) }) })), }, { label: t('translation|Load from URL'), component: (_jsx(Box, { pt: 2, children: _jsx(UploadFromUrl, { onLoaded: finishLoad, onCancel: () => setUploadFiles(false) }) })), }, ] }) }) })); }