feature/gui-configuration:支持从 GUI 配置程序。 #4
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -16,9 +16,10 @@ | |||||||
|     "@fortawesome/free-regular-svg-icons": "^6.2.1", |     "@fortawesome/free-regular-svg-icons": "^6.2.1", | ||||||
|     "@fortawesome/free-solid-svg-icons": "^6.2.1", |     "@fortawesome/free-solid-svg-icons": "^6.2.1", | ||||||
|     "@fortawesome/react-fontawesome": "^0.2.0", |     "@fortawesome/react-fontawesome": "^0.2.0", | ||||||
|     "@mui/material": "^5.11.2", |     "@mui/material": "^5.11.4", | ||||||
|     "@tauri-apps/api": "^1.2.0", |     "@tauri-apps/api": "^1.2.0", | ||||||
|     "clsx": "^1.2.1", |     "clsx": "^1.2.1", | ||||||
|  |     "debug": "^4.3.4", | ||||||
|     "ramda": "^0.28.0", |     "ramda": "^0.28.0", | ||||||
|     "react": "^18.2.0", |     "react": "^18.2.0", | ||||||
|     "react-async-hook": "^4.0.0", |     "react-async-hook": "^4.0.0", | ||||||
| @@ -29,6 +30,7 @@ | |||||||
|     "@emotion/babel-plugin-jsx-pragmatic": "^0.2.0", |     "@emotion/babel-plugin-jsx-pragmatic": "^0.2.0", | ||||||
|     "@emotion/serialize": "^1.1.1", |     "@emotion/serialize": "^1.1.1", | ||||||
|     "@tauri-apps/cli": "^1.2.2", |     "@tauri-apps/cli": "^1.2.2", | ||||||
|  |     "@types/debug": "^4.1.7", | ||||||
|     "@types/node": "^18.11.18", |     "@types/node": "^18.11.18", | ||||||
|     "@types/ramda": "^0.28.20", |     "@types/ramda": "^0.28.20", | ||||||
|     "@types/react": "^18.0.26", |     "@types/react": "^18.0.26", | ||||||
| @@ -36,13 +38,13 @@ | |||||||
|     "@vitejs/plugin-react": "^2.2.0", |     "@vitejs/plugin-react": "^2.2.0", | ||||||
|     "autoprefixer": "^10.4.13", |     "autoprefixer": "^10.4.13", | ||||||
|     "babel-plugin-macros": "^3.1.0", |     "babel-plugin-macros": "^3.1.0", | ||||||
|     "eslint-config-prettier": "^8.5.0", |     "eslint-config-prettier": "^8.6.0", | ||||||
|     "eslint-plugin-import": "^2.26.0", |     "eslint-plugin-import": "^2.27.4", | ||||||
|     "eslint-plugin-jsx-a11y": "^6.6.1", |     "eslint-plugin-jsx-a11y": "^6.7.1", | ||||||
|     "eslint-plugin-prettier": "^4.2.1", |     "eslint-plugin-prettier": "^4.2.1", | ||||||
|     "eslint-plugin-simple-import-sort": "^8.0.0", |     "eslint-plugin-simple-import-sort": "^8.0.0", | ||||||
|     "postcss": "^8.4.20", |     "postcss": "^8.4.21", | ||||||
|     "prettier": "^2.8.1", |     "prettier": "^2.8.3", | ||||||
|     "tailwindcss": "^3.2.4", |     "tailwindcss": "^3.2.4", | ||||||
|     "twin.macro": "^3.1.0", |     "twin.macro": "^3.1.0", | ||||||
|     "typescript": "^4.9.4", |     "typescript": "^4.9.4", | ||||||
|   | |||||||
							
								
								
									
										490
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										490
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,5 +1,13 @@ | |||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
|  | #[derive(Clone, Copy, Serialize, Deserialize, Debug)] | ||||||
|  | pub struct LedStripConfigOfBorders { | ||||||
|  |     pub top: Option<LedStripConfig>, | ||||||
|  |     pub bottom: Option<LedStripConfig>, | ||||||
|  |     pub left: Option<LedStripConfig>, | ||||||
|  |     pub right: Option<LedStripConfig>, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Clone, Copy, Serialize, Deserialize, Debug)] | #[derive(Clone, Copy, Serialize, Deserialize, Debug)] | ||||||
| pub struct LedStripConfig { | pub struct LedStripConfig { | ||||||
|     pub index: usize, |     pub index: usize, | ||||||
| @@ -13,23 +21,33 @@ pub struct DisplayConfig { | |||||||
|     pub index_of_display: usize, |     pub index_of_display: usize, | ||||||
|     pub display_width: usize, |     pub display_width: usize, | ||||||
|     pub display_height: usize, |     pub display_height: usize, | ||||||
|     pub top_led_strip: Option<LedStripConfig>, |     pub led_strip_of_borders: LedStripConfigOfBorders, | ||||||
|     pub bottom_led_strip: Option<LedStripConfig>, | } | ||||||
|     pub left_led_strip: Option<LedStripConfig>, |  | ||||||
|     pub right_led_strip: Option<LedStripConfig>, | impl LedStripConfigOfBorders { | ||||||
|  |     pub fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             top: None, | ||||||
|  |             bottom: None, | ||||||
|  |             left: None, | ||||||
|  |             right: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl DisplayConfig { | impl DisplayConfig { | ||||||
|     pub fn default(id: usize, index_of_display: usize, display_width: usize, display_height: usize) -> Self { |     pub fn default( | ||||||
|  |         id: usize, | ||||||
|  |         index_of_display: usize, | ||||||
|  |         display_width: usize, | ||||||
|  |         display_height: usize, | ||||||
|  |     ) -> Self { | ||||||
|         Self { |         Self { | ||||||
|             id, |             id, | ||||||
|             index_of_display, |             index_of_display, | ||||||
|             display_width, |             display_width, | ||||||
|             display_height, |             display_height, | ||||||
|             top_led_strip: None, |             led_strip_of_borders: LedStripConfigOfBorders::default(), | ||||||
|             bottom_led_strip: None, |  | ||||||
|             left_led_strip: None, |  | ||||||
|             right_led_strip: None, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ impl Screenshot { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn get_sample_points(config: DisplayConfig) -> ScreenSamplePoints { |     fn get_sample_points(config: DisplayConfig) -> ScreenSamplePoints { | ||||||
|         let top = match config.top_led_strip { |         let top = match config.led_strip_of_borders.top { | ||||||
|             Some(led_strip_config) => Self::get_one_edge_sample_points( |             Some(led_strip_config) => Self::get_one_edge_sample_points( | ||||||
|                 config.display_height / 8, |                 config.display_height / 8, | ||||||
|                 config.display_width, |                 config.display_width, | ||||||
| @@ -48,7 +48,7 @@ impl Screenshot { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let bottom: Vec<LedSamplePoints> = match config.bottom_led_strip { |         let bottom: Vec<LedSamplePoints> = match config.led_strip_of_borders.bottom { | ||||||
|             Some(led_strip_config) => { |             Some(led_strip_config) => { | ||||||
|                 let points = Self::get_one_edge_sample_points( |                 let points = Self::get_one_edge_sample_points( | ||||||
|                     config.display_height / 9, |                     config.display_height / 9, | ||||||
| @@ -73,7 +73,7 @@ impl Screenshot { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let left: Vec<LedSamplePoints> = match config.left_led_strip { |         let left: Vec<LedSamplePoints> = match config.led_strip_of_borders.left { | ||||||
|             Some(led_strip_config) => { |             Some(led_strip_config) => { | ||||||
|                 let points = Self::get_one_edge_sample_points( |                 let points = Self::get_one_edge_sample_points( | ||||||
|                     config.display_width / 16, |                     config.display_width / 16, | ||||||
| @@ -95,7 +95,7 @@ impl Screenshot { | |||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let right: Vec<LedSamplePoints> = match config.right_led_strip { |         let right: Vec<LedSamplePoints> = match config.led_strip_of_borders.right { | ||||||
|             Some(led_strip_config) => { |             Some(led_strip_config) => { | ||||||
|                 let points = Self::get_one_edge_sample_points( |                 let points = Self::get_one_edge_sample_points( | ||||||
|                     config.display_width / 16, |                     config.display_width / 16, | ||||||
| @@ -217,13 +217,13 @@ impl Screenshot { | |||||||
|  |  | ||||||
|     pub fn get_top_of_led_start_at(&self) -> usize { |     pub fn get_top_of_led_start_at(&self) -> usize { | ||||||
|         self.config |         self.config | ||||||
|             .top_led_strip |             .led_strip_of_borders.top | ||||||
|             .and_then(|c| Some(c.global_start_position)) |             .and_then(|c| Some(c.global_start_position)) | ||||||
|             .unwrap_or(0) |             .unwrap_or(0) | ||||||
|     } |     } | ||||||
|     pub fn get_top_of_led_end_at(&self) -> usize { |     pub fn get_top_of_led_end_at(&self) -> usize { | ||||||
|         self.config |         self.config | ||||||
|             .top_led_strip |             .led_strip_of_borders.top | ||||||
|             .and_then(|c| Some(c.global_end_position)) |             .and_then(|c| Some(c.global_end_position)) | ||||||
|             .unwrap_or(0) |             .unwrap_or(0) | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								src/configurator/components/completed-led-strip.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/configurator/components/completed-led-strip.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | import { isNil, splitEvery } from 'ramda'; | ||||||
|  | import { FC, useMemo } from 'react'; | ||||||
|  | import tw, { css, styled } from 'twin.macro'; | ||||||
|  | import { borders } from '../../constants/border'; | ||||||
|  | import { DisplayConfig } from '../models/display-config'; | ||||||
|  | import { LedStripConfig } from '../models/led-strip-config'; | ||||||
|  | import { ScreenshotDto } from '../models/screenshot.dto'; | ||||||
|  | import { LedStrip } from './led-strip'; | ||||||
|  | import { StyledPixel } from './styled-pixel'; | ||||||
|  |  | ||||||
|  | interface CompletedLedStripProps { | ||||||
|  |   screenshots: ScreenshotDto[]; | ||||||
|  |   onDisplayConfigChange?: (value: DisplayConfig) => void; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type BorderLedStrip = { | ||||||
|  |   pixels: [number, number, number][]; | ||||||
|  |   config: LedStripConfig | null; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const StyledContainer = styled.section( | ||||||
|  |   ({ rows, columns }: { rows: number; columns: number }) => [ | ||||||
|  |     tw`grid m-4 pb-2`, | ||||||
|  |     css` | ||||||
|  |       grid-template-columns: repeat(${columns}, 1fr); | ||||||
|  |       grid-template-rows: auto repeat(${rows}, 1fr); | ||||||
|  |     `, | ||||||
|  |   ], | ||||||
|  | ); | ||||||
|  | const StyledCompletedContainer = styled.section( | ||||||
|  |   tw`dark:bg-transparent shadow-xl border-gray-500 border rounded-full flex flex-wrap justify-around items-center mb-2`, | ||||||
|  |   css` | ||||||
|  |     grid-column: 1 / -1; | ||||||
|  |   `, | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | export const CompletedLedStrip: FC<CompletedLedStripProps> = ({ | ||||||
|  |   screenshots, | ||||||
|  |   onDisplayConfigChange, | ||||||
|  | }) => { | ||||||
|  |   const borderLedStrips: BorderLedStrip[] = useMemo(() => { | ||||||
|  |     return screenshots.flatMap((ss) => | ||||||
|  |       borders.map((b) => ({ | ||||||
|  |         pixels: splitEvery(3, Array.from(ss.colors[b])) as [number, number, number][], | ||||||
|  |         config: ss.config.led_strip_of_borders[b], | ||||||
|  |       })), | ||||||
|  |     ); | ||||||
|  |   }, [screenshots]); | ||||||
|  |   const ledCount = useMemo( | ||||||
|  |     () => borderLedStrips.reduce((prev, curr) => prev + curr.pixels.length, 0), | ||||||
|  |     [borderLedStrips], | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const completedPixels = useMemo(() => { | ||||||
|  |     const completed: [number, number, number][] = new Array(ledCount).fill([0, 0, 0]); | ||||||
|  |     borderLedStrips.forEach(({ pixels, config }) => { | ||||||
|  |       if (isNil(config)) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       if (config.global_start_position <= config.global_end_position) { | ||||||
|  |         pixels.forEach((color, i) => { | ||||||
|  |           completed[config.global_start_position + i] = color; | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         pixels.forEach((color, i) => { | ||||||
|  |           completed[config.global_start_position - i] = color; | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     return completed.map((color) => <StyledPixel rgb={color} />); | ||||||
|  |   }, [ledCount, borderLedStrips]); | ||||||
|  |  | ||||||
|  |   const strips = useMemo(() => { | ||||||
|  |     return borderLedStrips.map(({ config, pixels }, index) => ( | ||||||
|  |       <LedStrip | ||||||
|  |         key={index} | ||||||
|  |         colors={Uint8Array.from(pixels.flat())} | ||||||
|  |         config={config} | ||||||
|  |         css={css` | ||||||
|  |           grid-column-start: ${(config?.global_start_position ?? 0) + 1}; | ||||||
|  |           grid-column-end: ${(config?.global_end_position ?? 0) + 1}; | ||||||
|  |         `} | ||||||
|  |       /> | ||||||
|  |     )); | ||||||
|  |   }, [borderLedStrips]); | ||||||
|  |   return ( | ||||||
|  |     <StyledContainer rows={screenshots.length * borders.length} columns={ledCount}> | ||||||
|  |       <StyledCompletedContainer>{completedPixels}</StyledCompletedContainer> | ||||||
|  |       {strips} | ||||||
|  |     </StyledContainer> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @@ -1,11 +1,15 @@ | |||||||
| import { HTMLAttributes, useCallback, useMemo } from 'react'; | import { HTMLAttributes, useCallback, useMemo } from 'react'; | ||||||
| import { FC } from 'react'; | import { FC } from 'react'; | ||||||
| import { DisplayConfig } from '../models/display-config'; | import { DisplayConfig, LedStripConfigOfBorders } from '../models/display-config'; | ||||||
| import { LedStrip } from './led-strip'; | import { LedStrip } from './led-strip'; | ||||||
| import tw, { css, styled, theme } from 'twin.macro'; | import tw, { css, styled, theme } from 'twin.macro'; | ||||||
| import { ScreenshotDto } from '../models/screenshot.dto'; | import { ScreenshotDto } from '../models/screenshot.dto'; | ||||||
| import { LedStripEditor } from './led-strip-editor'; | import { LedStripEditor } from './led-strip-editor'; | ||||||
| import { LedStripConfig } from '../models/led-strip-config'; | import { LedStripConfig } from '../models/led-strip-config'; | ||||||
|  | import debug from 'debug'; | ||||||
|  | import { lensPath, lensProp, set, view } from 'ramda'; | ||||||
|  |  | ||||||
|  | const logger = debug('app:display-with-led-strips'); | ||||||
|  |  | ||||||
| export interface DisplayWithLedStripsProps | export interface DisplayWithLedStripsProps | ||||||
|   extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> { |   extends Omit<HTMLAttributes<HTMLElement>, 'onChange'> { | ||||||
| @@ -36,61 +40,60 @@ export const DisplayWithLedStrips: FC<DisplayWithLedStripsProps> = ({ | |||||||
|   ); |   ); | ||||||
|  |  | ||||||
|   const onLedStripConfigChange = useCallback( |   const onLedStripConfigChange = useCallback( | ||||||
|     ( |     (position: keyof LedStripConfigOfBorders, value: LedStripConfig | null) => { | ||||||
|       position: |       const xLens = lensPath< | ||||||
|         | 'top_led_strip' |         DisplayConfig, | ||||||
|         | 'left_led_strip' |         'led_strip_of_borders', | ||||||
|         | 'right_led_strip' |         keyof LedStripConfigOfBorders | ||||||
|         | 'bottom_led_strip', |       >(['led_strip_of_borders', position]); | ||||||
|       value: LedStripConfig | null, |       const c = set(xLens, value, config); | ||||||
|     ) => { |       logger('on change. prev: %o, curr: %o', view(xLens, config), value); | ||||||
|       const c = { ...config, [position]: value }; |  | ||||||
|       onChange?.(c); |       onChange?.(c); | ||||||
|     }, |     }, | ||||||
|     [config], |     [config], | ||||||
|   ); |   ); | ||||||
|   return ( |   return ( | ||||||
|     <StyledContainer {...htmlAttrs}> |     <StyledContainer {...htmlAttrs}> | ||||||
|       <img src={screenshotUrl} tw="row-start-3 col-start-3" /> |       <img src={screenshotUrl} tw="row-start-3 col-start-3 w-full" /> | ||||||
|       <LedStrip |       <LedStrip | ||||||
|         config={config.top_led_strip} |         config={config.led_strip_of_borders.top} | ||||||
|         colors={screenshot.colors.top} |         colors={screenshot.colors.top} | ||||||
|         tw="row-start-2 col-start-3" |         tw="row-start-2 col-start-3" | ||||||
|       /> |       /> | ||||||
|       <LedStrip |       <LedStrip | ||||||
|         config={config.left_led_strip} |         config={config.led_strip_of_borders.left} | ||||||
|         colors={screenshot.colors.left} |         colors={screenshot.colors.left} | ||||||
|         tw="row-start-3 col-start-2" |         tw="row-start-3 col-start-2" | ||||||
|       /> |       /> | ||||||
|       <LedStrip |       <LedStrip | ||||||
|         config={config.right_led_strip} |         config={config.led_strip_of_borders.right} | ||||||
|         colors={screenshot.colors.right} |         colors={screenshot.colors.right} | ||||||
|         tw="row-start-3 col-start-4" |         tw="row-start-3 col-start-4" | ||||||
|       /> |       /> | ||||||
|       <LedStrip |       <LedStrip | ||||||
|         config={config.bottom_led_strip} |         config={config.led_strip_of_borders.bottom} | ||||||
|         colors={screenshot.colors.bottom} |         colors={screenshot.colors.bottom} | ||||||
|         tw="row-start-4 col-start-3" |         tw="row-start-4 col-start-3" | ||||||
|       /> |       /> | ||||||
|       <LedStripEditor |       <LedStripEditor | ||||||
|         config={config.top_led_strip} |         config={config.led_strip_of_borders.top} | ||||||
|         tw="row-start-1 col-start-3" |         tw="row-start-1 col-start-3" | ||||||
|         onChange={(value) => onLedStripConfigChange('top_led_strip', value)} |         onChange={(value) => onLedStripConfigChange('top', value)} | ||||||
|       /> |       /> | ||||||
|       <LedStripEditor |       <LedStripEditor | ||||||
|         config={config.left_led_strip} |         config={config.led_strip_of_borders.left} | ||||||
|         tw="row-start-3 col-start-1" |         tw="row-start-3 col-start-1" | ||||||
|         onChange={(value) => onLedStripConfigChange('left_led_strip', value)} |         onChange={(value) => onLedStripConfigChange('left', value)} | ||||||
|       /> |       /> | ||||||
|       <LedStripEditor |       <LedStripEditor | ||||||
|         config={config.right_led_strip} |         config={config.led_strip_of_borders.right} | ||||||
|         tw="row-start-3 col-start-5" |         tw="row-start-3 col-start-5" | ||||||
|         onChange={(value) => onLedStripConfigChange('right_led_strip', value)} |         onChange={(value) => onLedStripConfigChange('right', value)} | ||||||
|       /> |       /> | ||||||
|       <LedStripEditor |       <LedStripEditor | ||||||
|         config={config.bottom_led_strip} |         config={config.led_strip_of_borders.bottom} | ||||||
|         tw="row-start-5 col-start-3" |         tw="row-start-5 col-start-3" | ||||||
|         onChange={(value) => onLedStripConfigChange('bottom_led_strip', value)} |         onChange={(value) => onLedStripConfigChange('bottom', value)} | ||||||
|       /> |       /> | ||||||
|     </StyledContainer> |     </StyledContainer> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ import { FC } from 'react'; | |||||||
| import { LedStripConfig } from '../models/led-strip-config'; | import { LedStripConfig } from '../models/led-strip-config'; | ||||||
| import tw, { css, styled } from 'twin.macro'; | import tw, { css, styled } from 'twin.macro'; | ||||||
| import { splitEvery } from 'ramda'; | import { splitEvery } from 'ramda'; | ||||||
|  | import { StyledPixel } from './styled-pixel'; | ||||||
|  |  | ||||||
| export interface LedStripProps extends HTMLAttributes<HTMLElement> { | export interface LedStripProps extends HTMLAttributes<HTMLElement> { | ||||||
|   config: LedStripConfig | null; |   config: LedStripConfig | null; | ||||||
| @@ -10,19 +11,10 @@ export interface LedStripProps extends HTMLAttributes<HTMLElement> { | |||||||
| } | } | ||||||
|  |  | ||||||
| const StyledContainer = styled.section( | const StyledContainer = styled.section( | ||||||
|   tw`dark:bg-transparent shadow-xl border-gray-500 border rounded-full flex flex-wrap justify-around items-center`, |   tw`dark:bg-transparent shadow-xl border-gray-500 border rounded-full flex flex-wrap justify-around items-center -mx-px -mt-px`, | ||||||
|   css``, |   css``, | ||||||
| ); | ); | ||||||
|  |  | ||||||
| const StyledPixel = styled.span( |  | ||||||
|   ({ rgb: [r, g, b] }: { rgb: [number, number, number] }) => [ |  | ||||||
|     tw`rounded-full h-3 w-3 bg-current block border border-gray-700`, |  | ||||||
|     css` |  | ||||||
|       color: rgb(${r}, ${g}, ${b}); |  | ||||||
|     `, |  | ||||||
|   ], |  | ||||||
| ); |  | ||||||
|  |  | ||||||
| export const LedStrip: FC<LedStripProps> = ({ config, colors, ...htmlAttrs }) => { | export const LedStrip: FC<LedStripProps> = ({ config, colors, ...htmlAttrs }) => { | ||||||
|   const pixels = useMemo(() => { |   const pixels = useMemo(() => { | ||||||
|     const pixels = splitEvery(3, Array.from(colors)) as Array<[number, number, number]>; |     const pixels = splitEvery(3, Array.from(colors)) as Array<[number, number, number]>; | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								src/configurator/components/styled-pixel.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/configurator/components/styled-pixel.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | import tw, { css, styled } from 'twin.macro'; | ||||||
|  |  | ||||||
|  | export const StyledPixel = styled.span( | ||||||
|  |   ({ rgb: [r, g, b] }: { rgb: [number, number, number] }) => [ | ||||||
|  |     tw`rounded-full h-3 w-3 bg-current block border border-gray-700`, | ||||||
|  |     css` | ||||||
|  |       color: rgb(${r}, ${g}, ${b}); | ||||||
|  |     `, | ||||||
|  |   ], | ||||||
|  | ); | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { invoke } from '@tauri-apps/api'; | import { invoke } from '@tauri-apps/api'; | ||||||
| import { FC, Fragment, useCallback, useEffect, useMemo, useState } from 'react'; | import { FC, Fragment, useCallback, useEffect, useMemo, useState } from 'react'; | ||||||
| import tw from 'twin.macro'; | import tw, { styled } from 'twin.macro'; | ||||||
| import { useAsync, useAsyncCallback } from 'react-async-hook'; | import { useAsync, useAsyncCallback } from 'react-async-hook'; | ||||||
| import { DisplayWithLedStrips } from './components/display-with-led-strips'; | import { DisplayWithLedStrips } from './components/display-with-led-strips'; | ||||||
| import { PickerConfiguration } from './models/picker-configuration'; | import { PickerConfiguration } from './models/picker-configuration'; | ||||||
| @@ -10,6 +10,7 @@ import { Alert, Snackbar } from '@mui/material'; | |||||||
| import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||||||
| import { faSpinner } from '@fortawesome/free-solid-svg-icons'; | import { faSpinner } from '@fortawesome/free-solid-svg-icons'; | ||||||
| import { update } from 'ramda'; | import { update } from 'ramda'; | ||||||
|  | import { CompletedLedStrip } from './components/completed-led-strip'; | ||||||
|  |  | ||||||
| const getPickerConfig = () => invoke<PickerConfiguration>('get_picker_config'); | const getPickerConfig = () => invoke<PickerConfiguration>('get_picker_config'); | ||||||
| const getScreenshotOfDisplays = () => | const getScreenshotOfDisplays = () => | ||||||
| @@ -26,6 +27,9 @@ const writePickerConfig = async (config: PickerConfiguration) => { | |||||||
|     config, |     config, | ||||||
|   }); |   }); | ||||||
| }; | }; | ||||||
|  | const StyledConfiguratorContainer = styled.section(tw`flex flex-col items-stretch`); | ||||||
|  |  | ||||||
|  | const StyledDisplayContainer = styled.section(tw`overflow-auto`); | ||||||
|  |  | ||||||
| export const Configurator: FC = () => { | export const Configurator: FC = () => { | ||||||
|   const { loading: pendingPickerConfig, result: savedPickerConfig } = useAsync( |   const { loading: pendingPickerConfig, result: savedPickerConfig } = useAsync( | ||||||
| @@ -91,13 +95,14 @@ export const Configurator: FC = () => { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Fragment> |     <StyledConfiguratorContainer> | ||||||
|       <section>{displays}</section>; |       <CompletedLedStrip screenshots={screenshotOfDisplays} /> | ||||||
|  |       <StyledDisplayContainer>{displays}</StyledDisplayContainer>; | ||||||
|       <Snackbar open={pendingGetLedColorsByConfig} autoHideDuration={3000}> |       <Snackbar open={pendingGetLedColorsByConfig} autoHideDuration={3000}> | ||||||
|         <Alert icon={<FontAwesomeIcon icon={faSpinner} />} sx={{ width: '100%' }}> |         <Alert icon={<FontAwesomeIcon icon={faSpinner} />} sx={{ width: '100%' }}> | ||||||
|           This is a success message! |           This is a success message! | ||||||
|         </Alert> |         </Alert> | ||||||
|       </Snackbar> |       </Snackbar> | ||||||
|     </Fragment> |     </StyledConfiguratorContainer> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,10 +1,16 @@ | |||||||
| import { LedStripConfig } from './led-strip-config'; | import { Borders } from '../../constants/border'; | ||||||
|  | import { LedStripConfig } from "./led-strip-config"; | ||||||
|  |  | ||||||
|  | export class LedStripConfigOfBorders implements Record<Borders, LedStripConfig | null> { | ||||||
|  |   constructor( | ||||||
|  |     public top: LedStripConfig | null = null, | ||||||
|  |     public bottom: LedStripConfig | null = null, | ||||||
|  |     public left: LedStripConfig | null = null, | ||||||
|  |     public right: LedStripConfig | null = null, | ||||||
|  |   ) {} | ||||||
|  | } | ||||||
| export class DisplayConfig { | export class DisplayConfig { | ||||||
|   top_led_strip: LedStripConfig | null = null; |   led_strip_of_borders = new LedStripConfigOfBorders(); | ||||||
|   bottom_led_strip: LedStripConfig | null = null; |  | ||||||
|   left_led_strip: LedStripConfig | null = null; |  | ||||||
|   right_led_strip: LedStripConfig | null = null; |  | ||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
|     public id: number, |     public id: number, | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								src/constants/border.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/constants/border.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | export const borders = ['top', 'right', 'bottom', 'left'] as const; | ||||||
|  | export type Borders = typeof borders[number]; | ||||||
		Reference in New Issue
	
	Block a user