Compare commits
3 Commits
3c7b3c76b9
...
13d32fb0e0
Author | SHA1 | Date | |
---|---|---|---|
|
13d32fb0e0 | ||
|
abee609e43 | ||
|
03fe09d1ce |
|
@ -80,6 +80,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-width: 0;
|
||||||
|
border-top-width: 1px;
|
||||||
|
border-color: var(--color-outline-variant);
|
||||||
|
border-style: solid;
|
||||||
|
width: 100%;
|
||||||
|
align-self: stretch;
|
||||||
|
&.dashed {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
&.dotted {
|
||||||
|
border-style: dotted;
|
||||||
|
}
|
||||||
|
&.vertical {
|
||||||
|
border-top-width: 0;
|
||||||
|
border-left-width: 1px;
|
||||||
|
width: auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:where(button, .button) {
|
:where(button, .button) {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: calc(var(--border-radius) * 2);
|
border-radius: calc(var(--border-radius) * 2);
|
||||||
|
|
|
@ -117,7 +117,7 @@ export function totalDuration(pattern: Pattern): number {
|
||||||
return reduce(
|
return reduce(
|
||||||
pattern.pulses,
|
pattern.pulses,
|
||||||
(former, pulse) => former + pulse.offset,
|
(former, pulse) => former + pulse.offset,
|
||||||
pattern.smoothRepeat ? 100 : 0,
|
pattern.smoothRepeat && pattern.pulses.length > 1 ? 100 : 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,58 @@
|
||||||
flex: 2;
|
flex: 2;
|
||||||
border-radius: calc(var(--border-radius) * 2);
|
border-radius: calc(var(--border-radius) * 2);
|
||||||
background-color: var(--color-surface-container);
|
background-color: var(--color-surface-container);
|
||||||
|
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: calc(var(--spacing) * 2);
|
gap: calc(var(--spacing) * 2);
|
||||||
|
.control_panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc(var(--spacing) * 2);
|
||||||
|
.button_row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: calc(var(--spacing) * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.detail_panel {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: calc(var(--spacing) * 2);
|
||||||
|
.detail_row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: calc(var(--spacing) * 2);
|
||||||
|
.detail_unit {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: calc(var(--spacing));
|
||||||
|
label {
|
||||||
|
min-width: 8em;
|
||||||
|
flex: 0 0 8em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.preview_panel {
|
||||||
|
min-height: 140px;
|
||||||
|
flex: 0 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.empty_promption {
|
.empty_promption {
|
||||||
flex: 2;
|
flex: 2;
|
||||||
border-radius: calc(var(--border-radius) * 2);
|
border-radius: calc(var(--border-radius) * 2);
|
||||||
background-color: var(--color-surface-container);
|
background-color: var(--color-surface-container);
|
||||||
|
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
import { useAtomValue } from 'jotai';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { FC } from 'react';
|
import { ask } from '@tauri-apps/plugin-dialog';
|
||||||
import { CurrentPatternAtom, Pattern } from '../../context/Patterns';
|
import dayjs from 'dayjs';
|
||||||
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
|
import { FC, useCallback, useMemo } from 'react';
|
||||||
|
import { NotificationType, useNotification } from '../../components/Notifications';
|
||||||
|
import PatternPreview from '../../components/PatternPreview';
|
||||||
|
import {
|
||||||
|
CurrentPatternAtom,
|
||||||
|
Pattern,
|
||||||
|
PatternsAtom,
|
||||||
|
SelectedPatternIdAtom,
|
||||||
|
totalDuration,
|
||||||
|
} from '../../context/Patterns';
|
||||||
import styles from './PatternDetail.module.css';
|
import styles from './PatternDetail.module.css';
|
||||||
|
|
||||||
const EmptyPromption: FC = () => {
|
const EmptyPromption: FC = () => {
|
||||||
|
@ -15,11 +26,80 @@ const EmptyPromption: FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Detail: FC<{ pattern: Pattern }> = ({ pattern }) => {
|
const Detail: FC<{ pattern: Pattern }> = ({ pattern }) => {
|
||||||
|
const { showToast } = useNotification();
|
||||||
|
const refreshPatterns = useSetAtom(PatternsAtom);
|
||||||
|
const resetSelected = useSetAtom(SelectedPatternIdAtom);
|
||||||
|
const patternDuration = useMemo(() => totalDuration(pattern), [pattern]);
|
||||||
|
const createTime = useMemo(
|
||||||
|
() => dayjs(pattern.createdAt).format('YYYY-MM-DD HH:mm:ss'),
|
||||||
|
[pattern],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDeleteAction = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const answer = await ask(
|
||||||
|
`The pattern ${pattern.name} will be deleted, and cannot be revoked. Are you sure?`,
|
||||||
|
{
|
||||||
|
title: 'Confirm action',
|
||||||
|
kind: 'warning',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (answer) {
|
||||||
|
await invoke('remove_pattern', { patternId: pattern.id });
|
||||||
|
showToast(NotificationType.SUCCESS, 'Pattern deleted.');
|
||||||
|
refreshPatterns();
|
||||||
|
resetSelected(null);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[delete pattern]', e);
|
||||||
|
showToast(NotificationType.ERROR, 'Failed to delete pattern. Please try again.');
|
||||||
|
}
|
||||||
|
}, [pattern]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.pattern_detail}>
|
<div className={styles.pattern_detail}>
|
||||||
<div></div>
|
<div className={styles.control_panel}>
|
||||||
<div></div>
|
<div className={styles.button_row}>
|
||||||
<div></div>
|
<button className="tonal">Edit Pattern</button>
|
||||||
|
<button className="tonal danger" onClick={handleDeleteAction}>
|
||||||
|
Delete Pattern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.button_row}>
|
||||||
|
<button className="tonal warn">Test Run</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.button_row}>
|
||||||
|
<button className="tonal secondary">Add to Channel A Playlist</button>
|
||||||
|
<button className="tonal secondary">Add to Channel B Playlist</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr className="dotted" />
|
||||||
|
<div className={styles.detail_panel}>
|
||||||
|
<div className={styles.detail_row}>
|
||||||
|
<div className={styles.detail_unit}>
|
||||||
|
<label>Created At</label>
|
||||||
|
<div className={styles.content}>{createTime}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.detail_row}>
|
||||||
|
<div className={styles.detail_unit}>
|
||||||
|
<label>Duration</label>
|
||||||
|
<div className={styles.content}>{(patternDuration / 1000).toFixed(2)} s</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.detail_row}>
|
||||||
|
<div className={styles.detail_unit}>
|
||||||
|
<label> </label>
|
||||||
|
<div className={styles.content}>
|
||||||
|
{pattern.pulses.length} key frame{pattern.pulses.length > 1 && 's'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr className="dotted" />
|
||||||
|
<div className={styles.preview_panel}>
|
||||||
|
<PatternPreview />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user