mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-27 13:38:40 +00:00
add pattern filter behaves like sounds tab
This commit is contained in:
parent
b45fd5ce3a
commit
9dca7eb878
@ -1,8 +1,7 @@
|
|||||||
import { DocumentDuplicateIcon, TrashIcon } from '@heroicons/react/20/solid';
|
|
||||||
import { useSettings } from '../../settings.mjs';
|
|
||||||
import {
|
import {
|
||||||
exportPatterns,
|
exportPatterns,
|
||||||
importPatterns,
|
importPatterns,
|
||||||
|
patternFilterName,
|
||||||
useActivePattern,
|
useActivePattern,
|
||||||
useViewingPatternData,
|
useViewingPatternData,
|
||||||
userPattern,
|
userPattern,
|
||||||
@ -11,6 +10,8 @@ import { useMemo } from 'react';
|
|||||||
import { getMetadata } from '../../metadata_parser';
|
import { getMetadata } from '../../metadata_parser';
|
||||||
import { useExamplePatterns } from '../useExamplePatterns';
|
import { useExamplePatterns } from '../useExamplePatterns';
|
||||||
import { parseJSON } from '../util.mjs';
|
import { parseJSON } from '../util.mjs';
|
||||||
|
import { ButtonGroup } from './Forms.jsx';
|
||||||
|
import { settingsMap, useSettings } from '../../settings.mjs';
|
||||||
|
|
||||||
function classNames(...classes) {
|
function classNames(...classes) {
|
||||||
return classes.filter(Boolean).join(' ');
|
return classes.filter(Boolean).join(' ');
|
||||||
@ -18,14 +19,22 @@ function classNames(...classes) {
|
|||||||
|
|
||||||
function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) {
|
function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) {
|
||||||
const meta = useMemo(() => getMetadata(pattern.code), [pattern]);
|
const meta = useMemo(() => getMetadata(pattern.code), [pattern]);
|
||||||
|
let title = meta.title;
|
||||||
|
if (title == null) {
|
||||||
|
title == pattern.hash;
|
||||||
|
}
|
||||||
|
if (title == null) {
|
||||||
|
const date = new Date(pattern.created_at);
|
||||||
|
if (isNaN(date)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
title = date.toLocaleDateString();
|
||||||
|
}
|
||||||
|
if (title == null) {
|
||||||
|
title = 'unnamed';
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return <>{`${pattern.id}: ${title} by ${Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous'}`}</>;
|
||||||
<>{`${pattern.id}: ${
|
|
||||||
meta.title ?? pattern.hash ?? pattern.created_at != null
|
|
||||||
? new Date(pattern.created_at).toLocaleDateString()
|
|
||||||
: 'unnamed'
|
|
||||||
} by ${Array.isArray(meta.by) ? meta.by.join(',') : 'Anonymous'}`}</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function PatternButton({ showOutline, onClick, pattern, showHiglight }) {
|
function PatternButton({ showOutline, onClick, pattern, showHiglight }) {
|
||||||
@ -81,7 +90,7 @@ export function PatternsTab({ context }) {
|
|||||||
const viewingPatternStore = useViewingPatternData();
|
const viewingPatternStore = useViewingPatternData();
|
||||||
const viewingPatternData = parseJSON(viewingPatternStore);
|
const viewingPatternData = parseJSON(viewingPatternStore);
|
||||||
|
|
||||||
const { userPatterns } = useSettings();
|
const { userPatterns, patternFilter } = useSettings();
|
||||||
const examplePatterns = useExamplePatterns();
|
const examplePatterns = useExamplePatterns();
|
||||||
const collections = examplePatterns.collections;
|
const collections = examplePatterns.collections;
|
||||||
|
|
||||||
@ -93,89 +102,91 @@ export function PatternsTab({ context }) {
|
|||||||
const isUserPattern = userPatterns[viewingPatternID] != null;
|
const isUserPattern = userPatterns[viewingPatternID] != null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-4 w-full dark:text-white text-stone-900 space-y-4 pb-4">
|
<div className="px-4 w-full dark:text-white text-stone-900 space-y-2 pb-4">
|
||||||
|
<ButtonGroup
|
||||||
|
value={patternFilter}
|
||||||
|
onChange={(value) => settingsMap.setKey('patternFilter', value)}
|
||||||
|
items={patternFilterName}
|
||||||
|
></ButtonGroup>
|
||||||
<section>
|
<section>
|
||||||
{viewingIDIsValid && (
|
{patternFilter === patternFilterName.user && (
|
||||||
<div className="flex items-center mb-2 space-x-2 overflow-auto">
|
<div>
|
||||||
<h1 className="text-xl">{`${viewingPatternID}`}</h1>
|
<div className="pr-4 space-x-4 border-b border-foreground mb-2 flex overflow-auto max-w-full items-center">
|
||||||
<div className="space-x-4 flex w-min">
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
label="Duplicate"
|
label="new"
|
||||||
|
onClick={() => {
|
||||||
|
const { data } = userPattern.createAndAddToDB();
|
||||||
|
updateCodeWindow(data);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ActionButton
|
||||||
|
label="duplicate"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const { data } = userPattern.duplicate(viewingPatternData);
|
const { data } = userPattern.duplicate(viewingPatternData);
|
||||||
updateCodeWindow(data);
|
updateCodeWindow(data);
|
||||||
}}
|
}}
|
||||||
labelIsHidden
|
/>
|
||||||
>
|
<ActionButton
|
||||||
<DocumentDuplicateIcon className="w-5 h-5" />
|
label="delete"
|
||||||
</ActionButton>
|
onClick={() => {
|
||||||
{isUserPattern && (
|
const { data } = userPattern.delete(viewingPatternID);
|
||||||
<ActionButton
|
updateCodeWindow({ ...data, collection: userPattern.collection });
|
||||||
label="Delete"
|
}}
|
||||||
onClick={() => {
|
/>
|
||||||
const { data } = userPattern.delete(viewingPatternID);
|
<label className="hover:opacity-50 cursor-pointer">
|
||||||
updateCodeWindow({ ...data, collection: userPattern.collection });
|
<input
|
||||||
}}
|
style={{ display: 'none' }}
|
||||||
labelIsHidden
|
type="file"
|
||||||
>
|
multiple
|
||||||
<TrashIcon className="w-5 h-5" />
|
accept="text/plain,application/json"
|
||||||
</ActionButton>
|
onChange={(e) => importPatterns(e.target.files)}
|
||||||
)}
|
/>
|
||||||
</div>
|
import
|
||||||
</div>
|
</label>
|
||||||
)}
|
<ActionButton label="export" onClick={exportPatterns} />
|
||||||
<PatternButtons
|
|
||||||
onClick={(id) => updateCodeWindow({ ...userPatterns[id], collection: userPattern.collection }, false)}
|
|
||||||
patterns={userPatterns}
|
|
||||||
started={context.started}
|
|
||||||
activePattern={activePattern}
|
|
||||||
viewingPatternID={viewingPatternID}
|
|
||||||
/>
|
|
||||||
<div className="pr-4 space-x-4 border-b border-foreground mb-2 h-8 flex overflow-auto max-w-full items-center">
|
|
||||||
<ActionButton
|
|
||||||
label="new"
|
|
||||||
onClick={() => {
|
|
||||||
const { data } = userPattern.createAndAddToDB();
|
|
||||||
updateCodeWindow(data);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ActionButton
|
|
||||||
label="clear"
|
|
||||||
onClick={() => {
|
|
||||||
const { data } = userPattern.clearAll();
|
|
||||||
updateCodeWindow(data);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label className="hover:opacity-50 cursor-pointer">
|
<ActionButton
|
||||||
<input
|
label="delete all"
|
||||||
style={{ display: 'none' }}
|
onClick={() => {
|
||||||
type="file"
|
const { data } = userPattern.clearAll();
|
||||||
multiple
|
updateCodeWindow(data);
|
||||||
accept="text/plain,application/json"
|
}}
|
||||||
onChange={(e) => importPatterns(e.target.files)}
|
|
||||||
/>
|
|
||||||
import
|
|
||||||
</label>
|
|
||||||
<ActionButton label="export" onClick={exportPatterns} />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{Array.from(collections.keys()).map((collection) => {
|
|
||||||
const patterns = collections.get(collection);
|
|
||||||
return (
|
|
||||||
<section key={collection}>
|
|
||||||
<h2 className="text-xl mb-2">{collection}</h2>
|
|
||||||
<div className="font-mono text-sm">
|
|
||||||
<PatternButtons
|
|
||||||
onClick={(id) => updateCodeWindow({ ...patterns[id], collection }, false)}
|
|
||||||
started={context.started}
|
|
||||||
patterns={patterns}
|
|
||||||
activePattern={activePattern}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
{/* {viewingIDIsValid && (
|
||||||
);
|
<div className="flex items-center mb-2 space-x-2 overflow-auto">
|
||||||
})}
|
<h1 className="text-xl">{`${viewingPatternID}`}</h1>
|
||||||
|
</div>
|
||||||
|
)} */}
|
||||||
|
|
||||||
|
<PatternButtons
|
||||||
|
onClick={(id) => updateCodeWindow({ ...userPatterns[id], collection: userPattern.collection }, false)}
|
||||||
|
patterns={userPatterns}
|
||||||
|
started={context.started}
|
||||||
|
activePattern={activePattern}
|
||||||
|
viewingPatternID={viewingPatternID}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{patternFilter !== patternFilterName.user &&
|
||||||
|
Array.from(collections.keys()).map((collection) => {
|
||||||
|
const patterns = collections.get(collection);
|
||||||
|
return (
|
||||||
|
<section key={collection} className="py-2">
|
||||||
|
<h2 className="text-xl mb-2">{collection}</h2>
|
||||||
|
<div className="font-mono text-sm">
|
||||||
|
<PatternButtons
|
||||||
|
onClick={(id) => updateCodeWindow({ ...patterns[id], collection }, false)}
|
||||||
|
started={context.started}
|
||||||
|
patterns={patterns}
|
||||||
|
activePattern={activePattern}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { useMemo } from 'react';
|
|||||||
import * as tunes from '../repl/tunes.mjs';
|
import * as tunes from '../repl/tunes.mjs';
|
||||||
|
|
||||||
export const stockPatterns = Object.fromEntries(
|
export const stockPatterns = Object.fromEntries(
|
||||||
Object.entries(tunes).map(([key, code], i) => [i, { id: i, code, collection: collectionName.stock }]),
|
Object.entries(tunes).map(([key, code], i) => [i, { id: i, code, collection: 'Stock Examples' }]),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const useExamplePatterns = () => {
|
export const useExamplePatterns = () => {
|
||||||
@ -14,7 +14,7 @@ export const useExamplePatterns = () => {
|
|||||||
const pats = new Map();
|
const pats = new Map();
|
||||||
pats.set(collectionName.featured, featuredPatterns);
|
pats.set(collectionName.featured, featuredPatterns);
|
||||||
pats.set(collectionName.public, publicPatterns);
|
pats.set(collectionName.public, publicPatterns);
|
||||||
pats.set(collectionName.stock, stockPatterns);
|
// pats.set(collectionName.stock, stockPatterns);
|
||||||
return pats;
|
return pats;
|
||||||
}, [featuredPatterns, publicPatterns]);
|
}, [featuredPatterns, publicPatterns]);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { persistentMap } from '@nanostores/persistent';
|
import { persistentMap } from '@nanostores/persistent';
|
||||||
import { useStore } from '@nanostores/react';
|
import { useStore } from '@nanostores/react';
|
||||||
import { register } from '@strudel/core';
|
import { register } from '@strudel/core';
|
||||||
|
import { patternFilterName } from './user_pattern_utils.mjs';
|
||||||
|
|
||||||
export const defaultAudioDeviceName = 'System Standard';
|
export const defaultAudioDeviceName = 'System Standard';
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ export const defaultSettings = {
|
|||||||
latestCode: '',
|
latestCode: '',
|
||||||
isZen: false,
|
isZen: false,
|
||||||
soundsFilter: 'all',
|
soundsFilter: 'all',
|
||||||
|
patternFilter: patternFilterName.community,
|
||||||
panelPosition: 'right',
|
panelPosition: 'right',
|
||||||
userPatterns: '{}',
|
userPatterns: '{}',
|
||||||
audioDeviceName: defaultAudioDeviceName,
|
audioDeviceName: defaultAudioDeviceName,
|
||||||
|
|||||||
@ -16,6 +16,11 @@ export const collectionName = {
|
|||||||
featured: 'Featured',
|
featured: 'Featured',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const patternFilterName = {
|
||||||
|
community: 'community',
|
||||||
|
user: 'user',
|
||||||
|
};
|
||||||
|
|
||||||
export let $viewingPatternData = persistentAtom(
|
export let $viewingPatternData = persistentAtom(
|
||||||
'viewingPatternData',
|
'viewingPatternData',
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user