// SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors import { Classes, Code, FormGroup, InputGroup, Intent, Tag } from '@blueprintjs/core'; import type { AriaButtonProps } from '@react-types/button'; import React, { useCallback, useRef } from 'react'; import { useButton } from 'react-aria'; import { FileNameValidationResult } from '../../pybricksMicropython/lib'; import { useI18n } from './i18n'; /** * Trims trailing and leading whitespace and replaces additional whitespace * with underscores. * @param value The input string. * @returns The fixed up string. */ function replaceSpaces(value: string): string { return value.trim().replaceAll(/\s+/g, '_'); } /** * Removes the file extension from a string. * @param value The input string. * @returns The fixed up string. */ function removeFileExtension(value: string): string { return value.replace(/\.\w+$/, ''); } /** * Trims trailing and leading whitespace and replaces groups of invalid * characters with underscores. * @param value The input string. * @returns The fixed up string. */ function replaceInvalidCharacters(value: string): string { return value.trim().replaceAll(/[^A-Za-z0-9-_]+/g, '_'); } type FixItButtonProps = Pick, 'onPress'>; const FixItButton: React.VoidFunctionComponent = (props) => { const i18n = useI18n(); const ref = useRef(null); const { buttonProps } = useButton( { ...props, elementType: 'a', }, ref, ); return {i18n.translate('helpText.fixIt')}; }; type FileNameHelpTextProps = { /** The file name in the input (without file extension). */ fileName: string; /** The result of the file name validation. */ validation: FileNameValidationResult; /** Called when the "fix it" link is clicked. */ onFix: (newName: string) => void; }; /** * Component that maps FileNameValidationResult to help message to display to user. */ const FileNameHelpText: React.VoidFunctionComponent = ({ fileName, validation, onFix, }) => { const i18n = useI18n(); const handleHasSpaces = useCallback(() => { onFix(replaceSpaces(fileName)); }, [fileName, onFix]); const handleHasFileExtension = useCallback(() => { onFix(removeFileExtension(fileName)); }, [fileName, onFix]); const handleHasInvalidCharacters = useCallback(() => { onFix(replaceInvalidCharacters(fileName)); }, [fileName, onFix]); switch (validation) { case FileNameValidationResult.IsOk: return <>{i18n.translate('helpText.isOk')}; case FileNameValidationResult.IsEmpty: return <>{i18n.translate('helpText.isEmpty')}; case FileNameValidationResult.HasSpaces: return ( <> {i18n.translate('helpText.hasSpaces')}{' '} ); case FileNameValidationResult.HasFileExtension: return ( <> {i18n.translate('helpText.hasFileExtension')}{' '} ); case FileNameValidationResult.HasInvalidFirstCharacter: return ( <> {i18n.translate('helpText.hasInvalidFirstCharacter', { letters: a…z, underscore: _, })} ); case FileNameValidationResult.HasInvalidCharacters: return ( <> {i18n.translate('helpText.hasInvalidCharacters', { letters: a…z, numbers: 0…9, dash: -, underscore: _, })}{' '} ); case FileNameValidationResult.AlreadyExists: return <>{i18n.translate('helpText.alreadyExists')}; } }; type FileNameFormGroupProps = { /** The file name in the input (without file extension). */ readonly fileName: string; /** The file extension (including leading ".") */ readonly fileExtension: string; /** The result of the file name validation. */ readonly validationResult: FileNameValidationResult; /** Ref to get handle to input (e.g to be able to call focus()) */ readonly inputRef?: React.RefObject; /** Called when the user changes the text in the input box. */ readonly onChange: (newName: string) => void; }; /** * Component used to get a valid new file name. */ const FileNameFormGroup: React.VoidFunctionComponent = ({ fileName, fileExtension, validationResult, inputRef, onChange, }) => { const i18n = useI18n(); const fileNameIntent = validationResult === FileNameValidationResult.IsOk ? Intent.NONE : Intent.DANGER; return ( } > {fileExtension}} onMouseDown={(e) => e.stopPropagation()} onChange={(e) => onChange(e.target.value)} /> ); }; export default FileNameFormGroup;