Compare commits

...

3 Commits

Author SHA1 Message Date
Vixalie
13d32fb0e0 basicly complete pattern detail presentation. 2025-03-11 22:37:57 +08:00
Vixalie
abee609e43 fix empty pattern duration calculation. 2025-03-11 22:37:37 +08:00
Vixalie
03fe09d1ce add hr component styles. 2025-03-11 22:13:51 +08:00
4 changed files with 153 additions and 7 deletions

View File

@ -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) {
border: none;
border-radius: calc(var(--border-radius) * 2);

View File

@ -117,7 +117,7 @@ export function totalDuration(pattern: Pattern): number {
return reduce(
pattern.pulses,
(former, pulse) => former + pulse.offset,
pattern.smoothRepeat ? 100 : 0,
pattern.smoothRepeat && pattern.pulses.length > 1 ? 100 : 0,
);
}

View File

@ -3,15 +3,58 @@
flex: 2;
border-radius: calc(var(--border-radius) * 2);
background-color: var(--color-surface-container);
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2);
display: flex;
flex-direction: column;
align-items: stretch;
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 {
flex: 2;
border-radius: calc(var(--border-radius) * 2);
background-color: var(--color-surface-container);
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2);
display: flex;
flex-direction: column;
align-items: stretch;

View File

@ -1,6 +1,17 @@
import { useAtomValue } from 'jotai';
import { FC } from 'react';
import { CurrentPatternAtom, Pattern } from '../../context/Patterns';
import { invoke } from '@tauri-apps/api/core';
import { ask } from '@tauri-apps/plugin-dialog';
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';
const EmptyPromption: FC = () => {
@ -15,11 +26,80 @@ const EmptyPromption: FC = () => {
};
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 (
<div className={styles.pattern_detail}>
<div></div>
<div></div>
<div></div>
<div className={styles.control_panel}>
<div className={styles.button_row}>
<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>&nbsp;</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>
);
};