Compare commits
2 Commits
a7137f0d51
...
bd025d6d71
Author | SHA1 | Date | |
---|---|---|---|
bd025d6d71 | |||
45f3f79a49 |
@ -4,6 +4,7 @@ import { invoke } from '@tauri-apps/api/tauri';
|
|||||||
import './App.css';
|
import './App.css';
|
||||||
import { Configurator } from './configurator/configurator';
|
import { Configurator } from './configurator/configurator';
|
||||||
import { ButtonSwitch } from './commons/components/button';
|
import { ButtonSwitch } from './commons/components/button';
|
||||||
|
import { fillParentCss } from './styles/fill-parent';
|
||||||
|
|
||||||
type Mode = 'Flowing' | 'Follow' | null;
|
type Mode = 'Flowing' | 'Follow' | null;
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ function App() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div css={[fillParentCss]} tw="box-border flex flex-col">
|
||||||
<div tw="flex justify-between">
|
<div tw="flex justify-between">
|
||||||
{ledStripColors.map((it) => (
|
{ledStripColors.map((it) => (
|
||||||
<span tw="h-8 flex-auto" style={{ backgroundColor: it }}></span>
|
<span tw="h-8 flex-auto" style={{ backgroundColor: it }}></span>
|
||||||
@ -64,7 +65,7 @@ function App() {
|
|||||||
<ButtonSwitch onClick={() => switchCurrentMode('Follow')}>Follow</ButtonSwitch>
|
<ButtonSwitch onClick={() => switchCurrentMode('Follow')}>Follow</ButtonSwitch>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div tw="flex gap-5 justify-center">
|
<div css={[fillParentCss]}>
|
||||||
<Configurator />
|
<Configurator />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
62
src/configurator/components/completed-container.tsx
Normal file
62
src/configurator/components/completed-container.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { isNil } from 'ramda';
|
||||||
|
import { FC, Fragment, useMemo } from 'react';
|
||||||
|
import tw, { css, styled } from 'twin.macro';
|
||||||
|
import { useLedCount } from '../contents/led-count';
|
||||||
|
import { PixelRgb } from '../models/pixel-rgb';
|
||||||
|
import { StyledPixel } from './styled-pixel';
|
||||||
|
import { BorderLedStrip } from './completed-led-strip';
|
||||||
|
|
||||||
|
const StyledCompletedContainerBorder = styled.section(
|
||||||
|
tw`dark:shadow-xl border-gray-600 bg-black border rounded-full flex flex-nowrap justify-around items-center p-px h-3 mb-1`,
|
||||||
|
css`
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 1 / span 1;
|
||||||
|
justify-self: stretch;
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
interface StyledCompletedContainerProps {
|
||||||
|
borderLedStrips: BorderLedStrip[];
|
||||||
|
overrideBorderLedStrips?: BorderLedStrip[];
|
||||||
|
}
|
||||||
|
export const CompletedContainer: FC<StyledCompletedContainerProps> = ({
|
||||||
|
borderLedStrips,
|
||||||
|
overrideBorderLedStrips,
|
||||||
|
}) => {
|
||||||
|
const { ledCount } = useLedCount();
|
||||||
|
const completedPixels = useMemo(() => {
|
||||||
|
const completed: PixelRgb[] = new Array(ledCount).fill([0, 0, 0]);
|
||||||
|
(overrideBorderLedStrips ?? 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, i) => (
|
||||||
|
<StyledPixel
|
||||||
|
rgb={color}
|
||||||
|
key={i}
|
||||||
|
css={css`
|
||||||
|
grid-row-start: 1;
|
||||||
|
grid-column-start: ${i + 1};
|
||||||
|
align-self: flex-start;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}, [ledCount, borderLedStrips, overrideBorderLedStrips]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<StyledCompletedContainerBorder />
|
||||||
|
{completedPixels}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { isNil, lensPath, set, splitEvery, update } from 'ramda';
|
import { lensPath, set, splitEvery, update } from 'ramda';
|
||||||
import { FC, useEffect, useMemo, useState } from 'react';
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
import tw, { css, styled } from 'twin.macro';
|
import tw, { css, styled } from 'twin.macro';
|
||||||
import { Borders, borders } from '../../constants/border';
|
import { Borders, borders } from '../../constants/border';
|
||||||
@ -8,8 +8,8 @@ import { DisplayConfig, LedStripConfigOfBorders } from '../models/display-config
|
|||||||
import { LedStripConfig } from '../models/led-strip-config';
|
import { LedStripConfig } from '../models/led-strip-config';
|
||||||
import { PixelRgb } from '../models/pixel-rgb';
|
import { PixelRgb } from '../models/pixel-rgb';
|
||||||
import { ScreenshotDto } from '../models/screenshot.dto';
|
import { ScreenshotDto } from '../models/screenshot.dto';
|
||||||
|
import { CompletedContainer } from './completed-container';
|
||||||
import { DraggableStrip } from './draggable-strip';
|
import { DraggableStrip } from './draggable-strip';
|
||||||
import { StyledPixel } from './styled-pixel';
|
|
||||||
|
|
||||||
export const logger = debug('app:completed-led-strip');
|
export const logger = debug('app:completed-led-strip');
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ interface CompletedLedStripProps {
|
|||||||
onDisplayConfigChange?: (value: DisplayConfig) => void;
|
onDisplayConfigChange?: (value: DisplayConfig) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type BorderLedStrip = {
|
export type BorderLedStrip = {
|
||||||
pixels: PixelRgb[];
|
pixels: PixelRgb[];
|
||||||
config: LedStripConfig | null;
|
config: LedStripConfig | null;
|
||||||
};
|
};
|
||||||
@ -26,20 +26,13 @@ type BorderLedStrip = {
|
|||||||
const StyledContainer = styled.section(
|
const StyledContainer = styled.section(
|
||||||
({ rows, columns }: { rows: number; columns: number }) => [
|
({ rows, columns }: { rows: number; columns: number }) => [
|
||||||
tw`grid m-4 pb-2 items-center justify-items-center select-none`,
|
tw`grid m-4 pb-2 items-center justify-items-center select-none`,
|
||||||
|
tw`overflow-x-auto overflow-y-hidden flex-none`,
|
||||||
css`
|
css`
|
||||||
grid-template-columns: repeat(${columns}, 1fr);
|
grid-template-columns: repeat(${columns}, 0.5em);
|
||||||
grid-template-rows: auto repeat(${rows}, 1fr);
|
grid-template-rows: auto repeat(${rows}, 0.5em);
|
||||||
`,
|
`,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
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;
|
|
||||||
justify-self: stretch;
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
|
|
||||||
export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
||||||
screenshots,
|
screenshots,
|
||||||
onDisplayConfigChange,
|
onDisplayConfigChange,
|
||||||
@ -56,6 +49,18 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
() => borderLedStrips.reduce((prev, curr) => prev + curr.pixels.length, 0),
|
() => borderLedStrips.reduce((prev, curr) => prev + curr.pixels.length, 0),
|
||||||
[borderLedStrips],
|
[borderLedStrips],
|
||||||
);
|
);
|
||||||
|
const maxIndex = useMemo(
|
||||||
|
() =>
|
||||||
|
Math.max(
|
||||||
|
...borderLedStrips
|
||||||
|
.map((s) => [
|
||||||
|
s.config?.global_end_position ?? 0,
|
||||||
|
s.config?.global_start_position ?? 0,
|
||||||
|
])
|
||||||
|
.flat(),
|
||||||
|
),
|
||||||
|
[borderLedStrips],
|
||||||
|
);
|
||||||
|
|
||||||
const { setLedCount } = useLedCount();
|
const { setLedCount } = useLedCount();
|
||||||
// setLedCount for context
|
// setLedCount for context
|
||||||
@ -66,26 +71,6 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
const [overrideBorderLedStrips, setOverrideBorderLedStrips] =
|
const [overrideBorderLedStrips, setOverrideBorderLedStrips] =
|
||||||
useState<BorderLedStrip[]>();
|
useState<BorderLedStrip[]>();
|
||||||
|
|
||||||
const completedPixels = useMemo(() => {
|
|
||||||
const completed: PixelRgb[] = new Array(ledCount).fill([0, 0, 0]);
|
|
||||||
(overrideBorderLedStrips ?? 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, i) => <StyledPixel rgb={color} key={i} />);
|
|
||||||
}, [ledCount, borderLedStrips, overrideBorderLedStrips]);
|
|
||||||
|
|
||||||
const strips = useMemo(() => {
|
const strips = useMemo(() => {
|
||||||
return borderLedStrips.map(({ config, pixels }, index) =>
|
return borderLedStrips.map(({ config, pixels }, index) =>
|
||||||
config ? (
|
config ? (
|
||||||
@ -123,11 +108,12 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
setOverrideBorderLedStrips(undefined);
|
setOverrideBorderLedStrips(undefined);
|
||||||
}, [borderLedStrips]);
|
}, [borderLedStrips]);
|
||||||
return (
|
return (
|
||||||
<StyledContainer rows={screenshots.length * borders.length} columns={ledCount}>
|
<StyledContainer rows={screenshots.length * borders.length} columns={maxIndex + 1}>
|
||||||
<StyledCompletedContainer>{completedPixels}</StyledCompletedContainer>
|
<CompletedContainer
|
||||||
|
borderLedStrips={borderLedStrips}
|
||||||
|
overrideBorderLedStrips={overrideBorderLedStrips}
|
||||||
|
/>
|
||||||
{strips}
|
{strips}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HTMLAttributes, useCallback } from 'react';
|
import { HTMLAttributes, MouseEventHandler, useCallback } from 'react';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { LedStripConfig } from '../models/led-strip-config';
|
import { LedStripConfig } from '../models/led-strip-config';
|
||||||
import tw, { styled } from 'twin.macro';
|
import tw, { styled } from 'twin.macro';
|
||||||
@ -26,36 +26,54 @@ export const LedStripEditor: FC<LedStripEditorProps> = ({
|
|||||||
onChange,
|
onChange,
|
||||||
...htmlAttrs
|
...htmlAttrs
|
||||||
}) => {
|
}) => {
|
||||||
const addLed = useCallback(() => {
|
const addLed: MouseEventHandler = useCallback(
|
||||||
|
(ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
const delta = ev.button === 2 ? 10 : 1;
|
||||||
if (config) {
|
if (config) {
|
||||||
if (config.global_start_position <= config.global_end_position) {
|
if (config.global_start_position <= config.global_end_position) {
|
||||||
onChange?.({ ...config, global_end_position: config.global_end_position + 1 });
|
onChange?.({
|
||||||
|
...config,
|
||||||
|
global_end_position: config.global_end_position + delta,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
onChange?.({
|
onChange?.({
|
||||||
...config,
|
...config,
|
||||||
global_start_position: config.global_start_position + 1,
|
global_start_position: config.global_start_position + delta,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onChange?.(new LedStripConfig(0, 0, 0));
|
onChange?.(new LedStripConfig(0, 0, 0));
|
||||||
}
|
}
|
||||||
}, [config, onChange]);
|
},
|
||||||
const removeLed = useCallback(() => {
|
[config, onChange],
|
||||||
|
);
|
||||||
|
const removeLed: MouseEventHandler = useCallback(
|
||||||
|
(ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
const delta = ev.button === 2 ? 10 : 1;
|
||||||
if (!config) {
|
if (!config) {
|
||||||
onChange?.(null);
|
onChange?.(null);
|
||||||
} else if (Math.abs(config.global_start_position - config.global_end_position) <= 1) {
|
} else if (
|
||||||
|
Math.abs(config.global_start_position - config.global_end_position) <= delta
|
||||||
|
) {
|
||||||
onChange?.(null);
|
onChange?.(null);
|
||||||
} else {
|
} else {
|
||||||
if (config.global_start_position <= config.global_end_position) {
|
if (config.global_start_position <= config.global_end_position) {
|
||||||
onChange?.({ ...config, global_end_position: config.global_end_position - 1 });
|
onChange?.({
|
||||||
|
...config,
|
||||||
|
global_end_position: config.global_end_position - delta,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
onChange?.({
|
onChange?.({
|
||||||
...config,
|
...config,
|
||||||
global_start_position: config.global_start_position - 1,
|
global_start_position: config.global_start_position - delta,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [config, onChange]);
|
},
|
||||||
|
[config, onChange],
|
||||||
|
);
|
||||||
const reverse = useCallback(() => {
|
const reverse = useCallback(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return;
|
return;
|
||||||
@ -69,10 +87,10 @@ export const LedStripEditor: FC<LedStripEditorProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer {...htmlAttrs}>
|
<StyledContainer {...htmlAttrs}>
|
||||||
<StyledButton title="Add LED" onClick={addLed}>
|
<StyledButton title="Add LED" onClick={addLed} onContextMenu={addLed}>
|
||||||
<FontAwesomeIcon icon={faPlus} />
|
<FontAwesomeIcon icon={faPlus} />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton title="Remove LED" onClick={removeLed}>
|
<StyledButton title="Remove LED" onClick={removeLed} onContextMenu={removeLed}>
|
||||||
<FontAwesomeIcon icon={faMinus} />
|
<FontAwesomeIcon icon={faMinus} />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
<StyledButton title="Reverse" onClick={reverse}>
|
<StyledButton title="Reverse" onClick={reverse}>
|
||||||
|
@ -14,6 +14,7 @@ import { CompletedLedStrip } from './components/completed-led-strip';
|
|||||||
import { LedCountProvider } from './contents/led-count';
|
import { LedCountProvider } from './contents/led-count';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { useSnackbar } from 'notistack';
|
import { useSnackbar } from 'notistack';
|
||||||
|
import { fillParentCss } from '../styles/fill-parent';
|
||||||
|
|
||||||
const logger = debug('app:configurator');
|
const logger = debug('app:configurator');
|
||||||
|
|
||||||
@ -33,7 +34,8 @@ const writePickerConfig = async (config: PickerConfiguration) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const StyledConfiguratorContainer = styled.section(
|
const StyledConfiguratorContainer = styled.section(
|
||||||
tw`flex flex-col items-stretch relative`,
|
tw`flex flex-col items-stretch relative overflow-hidden`,
|
||||||
|
fillParentCss,
|
||||||
);
|
);
|
||||||
|
|
||||||
const StyledDisplayContainer = styled.section(tw`overflow-auto`);
|
const StyledDisplayContainer = styled.section(tw`overflow-auto`);
|
||||||
@ -117,7 +119,7 @@ const ConfiguratorInner: FC = () => {
|
|||||||
screenshots={screenshotOfDisplays}
|
screenshots={screenshotOfDisplays}
|
||||||
onDisplayConfigChange={onDisplayConfigChange}
|
onDisplayConfigChange={onDisplayConfigChange}
|
||||||
/>
|
/>
|
||||||
<StyledDisplayContainer>{displays}</StyledDisplayContainer>
|
<StyledDisplayContainer tw="overflow-y-auto">{displays}</StyledDisplayContainer>
|
||||||
<Fab
|
<Fab
|
||||||
aria-label="reset"
|
aria-label="reset"
|
||||||
size="small"
|
size="small"
|
||||||
|
3
src/styles/fill-parent.ts
Normal file
3
src/styles/fill-parent.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import tw from 'twin.macro';
|
||||||
|
|
||||||
|
export const fillParentCss = tw`w-full h-full overflow-hidden`;
|
@ -1,13 +1,28 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Global, css } from '@emotion/react';
|
import { Global, css } from '@emotion/react';
|
||||||
import tw, { theme, GlobalStyles as BaseStyles } from 'twin.macro';
|
import tw, { theme, GlobalStyles as BaseStyles } from 'twin.macro';
|
||||||
|
import { fillParentCss } from './fill-parent';
|
||||||
|
|
||||||
const customStyles = css({
|
const customStyles = css({
|
||||||
body: {
|
body: {
|
||||||
WebkitTapHighlightColor: theme`colors.purple.500`,
|
WebkitTapHighlightColor: theme`colors.purple.500`,
|
||||||
...tw`antialiased`,
|
...tw`antialiased`,
|
||||||
...tw`dark:bg-dark-800 bg-dark-100 dark:text-gray-100 text-gray-800`,
|
...tw`dark:bg-dark-800 bg-dark-100 dark:text-gray-100 text-gray-800`,
|
||||||
|
...fillParentCss,
|
||||||
},
|
},
|
||||||
|
html: {
|
||||||
|
...fillParentCss,
|
||||||
|
},
|
||||||
|
'#root': {
|
||||||
|
...fillParentCss,
|
||||||
|
},
|
||||||
|
'::-webkit-scrollbar': tw`w-1.5 h-1.5`,
|
||||||
|
'::-webkit-scrollbar-button': tw`hidden`,
|
||||||
|
'::-webkit-scrollbar-track': tw`bg-transparent`,
|
||||||
|
'::-webkit-scrollbar-track-piece': tw`bg-transparent`,
|
||||||
|
'::-webkit-scrollbar-thumb': tw`bg-gray-300 dark:(bg-gray-700/50 hover:bg-gray-700/90 active:bg-gray-700/100) transition-colors rounded-full`,
|
||||||
|
'::-webkit-scrollbar-corner': tw`bg-gray-600`,
|
||||||
|
'::-webkit-resizer': tw`hidden`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const GlobalStyles = () => (
|
const GlobalStyles = () => (
|
||||||
|
Loading…
Reference in New Issue
Block a user