basicly complete pattern detail presentation.
This commit is contained in:
		@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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> </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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user