87 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { HTMLAttributes, useCallback } from 'react';
 | |
| import { FC } from 'react';
 | |
| import { LedStripConfig } from '../models/led-strip-config';
 | |
| import tw, { styled } from 'twin.macro';
 | |
| import { faLeftRight, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';
 | |
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 | |
| 
 | |
| export interface LedStripEditorProps
 | |
|   extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> {
 | |
|   config: LedStripConfig | null;
 | |
|   onChange?: (config: LedStripConfig | null) => void;
 | |
| }
 | |
| 
 | |
| const StyledContainer = styled.section(
 | |
|   tw`flex flex-wrap gap-2 self-start justify-self-start`,
 | |
| );
 | |
| 
 | |
| const StyledButton = styled.button(
 | |
|   tw`
 | |
| bg-yellow-500 dark:bg-amber-600 rounded-full h-4 w-4 text-xs shadow select-none`,
 | |
|   tw`hocus:scale-105 hocus:active:scale-95 active:bg-amber-600 active:dark:bg-amber-500`,
 | |
| );
 | |
| 
 | |
| export const LedStripEditor: FC<LedStripEditorProps> = ({
 | |
|   config,
 | |
|   onChange,
 | |
|   ...htmlAttrs
 | |
| }) => {
 | |
|   const addLed = useCallback(() => {
 | |
|     if (config) {
 | |
|       if (config.global_start_position <= config.global_end_position) {
 | |
|         onChange?.({ ...config, global_end_position: config.global_end_position + 1 });
 | |
|       } else {
 | |
|         onChange?.({
 | |
|           ...config,
 | |
|           global_start_position: config.global_start_position + 1,
 | |
|         });
 | |
|       }
 | |
|     } else {
 | |
|       onChange?.(new LedStripConfig(0, 0, 0));
 | |
|     }
 | |
|   }, [config, onChange]);
 | |
|   const removeLed = useCallback(() => {
 | |
|     if (!config) {
 | |
|       onChange?.(null);
 | |
|     } else if (Math.abs(config.global_start_position - config.global_end_position) <= 1) {
 | |
|       onChange?.(null);
 | |
|     } else {
 | |
|       if (config.global_start_position <= config.global_end_position) {
 | |
|         onChange?.({ ...config, global_end_position: config.global_end_position - 1 });
 | |
|       } else {
 | |
|         onChange?.({
 | |
|           ...config,
 | |
|           global_start_position: config.global_start_position - 1,
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   }, [config, onChange]);
 | |
|   const reverse = useCallback(() => {
 | |
|     if (!config) {
 | |
|       return;
 | |
|     }
 | |
|     onChange?.({
 | |
|       ...config,
 | |
|       global_start_position: config.global_end_position,
 | |
|       global_end_position: config.global_start_position,
 | |
|     });
 | |
|   }, [config, onChange]);
 | |
| 
 | |
|   return (
 | |
|     <StyledContainer {...htmlAttrs}>
 | |
|       <StyledButton title="Add LED" onClick={addLed}>
 | |
|         <FontAwesomeIcon icon={faPlus} />
 | |
|       </StyledButton>
 | |
|       <StyledButton title="Remove LED" onClick={removeLed}>
 | |
|         <FontAwesomeIcon icon={faMinus} />
 | |
|       </StyledButton>
 | |
|       <StyledButton title="Reverse" onClick={reverse}>
 | |
|         <FontAwesomeIcon icon={faLeftRight} />
 | |
|       </StyledButton>
 | |
|       {`s: ${config?.global_start_position ?? 'x'}, e: ${
 | |
|         config?.global_end_position ?? 'x'
 | |
|       }`}
 | |
|     </StyledContainer>
 | |
|   );
 | |
| };
 |