Files
strudel/website/src/repl/panel/PatternsTab.jsx
T
Jade (Rose) Rowland b45fd5ce3a fix invalid date
2024-01-20 13:00:33 -05:00

182 lines
5.9 KiB
React

import { DocumentDuplicateIcon, TrashIcon } from '@heroicons/react/20/solid';
import { useSettings } from '../../settings.mjs';
import {
exportPatterns,
importPatterns,
useActivePattern,
useViewingPatternData,
userPattern,
} from '../../user_pattern_utils.mjs';
import { useMemo } from 'react';
import { getMetadata } from '../../metadata_parser';
import { useExamplePatterns } from '../useExamplePatterns';
import { parseJSON } from '../util.mjs';
function classNames(...classes) {
return classes.filter(Boolean).join(' ');
}
function PatternLabel({ pattern } /* : { pattern: Tables<'code'> } */) {
const meta = useMemo(() => getMetadata(pattern.code), [pattern]);
return (
<>{`${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 }) {
return (
<a
className={classNames(
'mr-4 hover:opacity-50 cursor-pointer block',
showOutline && 'outline outline-1',
showHiglight && 'bg-selection',
)}
onClick={onClick}
>
<PatternLabel pattern={pattern} />
</a>
);
}
function PatternButtons({ patterns, activePattern, onClick, started }) {
const viewingPatternStore = useViewingPatternData();
const viewingPatternData = parseJSON(viewingPatternStore);
const viewingPatternID = viewingPatternData.id;
return (
<div className="font-mono text-sm">
{Object.values(patterns)
.reverse()
.map((pattern) => {
const id = pattern.id;
return (
<PatternButton
pattern={pattern}
key={id}
showHiglight={id === viewingPatternID}
showOutline={id === activePattern && started}
onClick={() => onClick(id)}
/>
);
})}
</div>
);
}
function ActionButton({ children, onClick, label, labelIsHidden }) {
return (
<button className="hover:opacity-50" onClick={onClick} title={label}>
{labelIsHidden !== true && label}
{children}
</button>
);
}
export function PatternsTab({ context }) {
const activePattern = useActivePattern();
const viewingPatternStore = useViewingPatternData();
const viewingPatternData = parseJSON(viewingPatternStore);
const { userPatterns } = useSettings();
const examplePatterns = useExamplePatterns();
const collections = examplePatterns.collections;
const updateCodeWindow = (patternData, reset = false) => {
context.handleUpdate(patternData, reset);
};
const viewingPatternID = viewingPatternData?.id;
const viewingIDIsValid = userPattern.isValidID(viewingPatternID);
const isUserPattern = userPatterns[viewingPatternID] != null;
return (
<div className="px-4 w-full dark:text-white text-stone-900 space-y-4 pb-4">
<section>
{viewingIDIsValid && (
<div className="flex items-center mb-2 space-x-2 overflow-auto">
<h1 className="text-xl">{`${viewingPatternID}`}</h1>
<div className="space-x-4 flex w-min">
<ActionButton
label="Duplicate"
onClick={() => {
const { data } = userPattern.duplicate(viewingPatternData);
updateCodeWindow(data);
}}
labelIsHidden
>
<DocumentDuplicateIcon className="w-5 h-5" />
</ActionButton>
{isUserPattern && (
<ActionButton
label="Delete"
onClick={() => {
const { data } = userPattern.delete(viewingPatternID);
updateCodeWindow({ ...data, collection: userPattern.collection });
}}
labelIsHidden
>
<TrashIcon className="w-5 h-5" />
</ActionButton>
)}
</div>
</div>
)}
<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">
<input
style={{ display: 'none' }}
type="file"
multiple
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>
</section>
);
})}
</div>
);
}