mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-27 16:58:46 +00:00
Projects (#192)
* Add file save UI and drafts project * Fix build * Create SearchList component as abstraction * Add project page and file tree view * Create Locale context * Create Theme context * Create Version context * Create Title context * Create Project context * Store current file in project context * Fix issues when renaming file and implement deleting * Style improvements * Make all project strings translatable * Fix z-index
This commit is contained in:
@@ -1,21 +1,19 @@
|
||||
import config from '../../config.json'
|
||||
import { ToolCard } from '../components'
|
||||
import { locale } from '../Locales'
|
||||
import { useLocale, useTitle } from '../contexts'
|
||||
import { cleanUrl } from '../Utils'
|
||||
|
||||
type WorldgenProps = {
|
||||
interface Props {
|
||||
category: string,
|
||||
lang: string,
|
||||
changeTitle: (title: string) => unknown,
|
||||
path?: string,
|
||||
}
|
||||
export function Category({ category, lang, changeTitle }: WorldgenProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
changeTitle(loc('title.generator_category', loc(category)))
|
||||
export function Category({ category }: Props) {
|
||||
const { locale } = useLocale()
|
||||
useTitle(locale('title.generator_category', locale(category)))
|
||||
return <main>
|
||||
<div class="category">
|
||||
{config.generators.filter(g => g.category === category).map(g =>
|
||||
<ToolCard title={loc(g.id)} link={cleanUrl(g.url)} />
|
||||
<ToolCard title={locale(g.id)} link={cleanUrl(g.url)} />
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { marked } from 'marked'
|
||||
import { useEffect, useMemo, useState } from 'preact/hooks'
|
||||
import { Ad, Btn, ErrorPanel, Octicon, TextInput } from '../components'
|
||||
import { locale } from '../Locales'
|
||||
import type { ChangelogEntry, ChangelogVersion, VersionId } from '../services'
|
||||
import { useLocale, useTitle } from '../contexts'
|
||||
import type { ChangelogEntry, ChangelogVersion } from '../services'
|
||||
import { getChangelogs } from '../services'
|
||||
import { hashString } from '../Utils'
|
||||
|
||||
type ChangelogProps = {
|
||||
interface Props {
|
||||
path?: string,
|
||||
lang: string,
|
||||
changeTitle: (title: string, versions?: VersionId[]) => unknown,
|
||||
}
|
||||
export function Changelog({ lang, changeTitle }: ChangelogProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
export function Changelog({}: Props) {
|
||||
const { locale } = useLocale()
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
changeTitle(loc('title.changelog'))
|
||||
useTitle(locale('title.changelog'))
|
||||
|
||||
const [changelogs, setChangelogs] = useState<ChangelogEntry[]>([])
|
||||
useEffect(() => {
|
||||
@@ -61,7 +59,7 @@ export function Changelog({ lang, changeTitle }: ChangelogProps) {
|
||||
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
|
||||
<div class="changelog-controls">
|
||||
<div class="changelog-query">
|
||||
<TextInput class="btn btn-input changelog-search" list="sound-list" placeholder={loc('changelog.search')}
|
||||
<TextInput class="btn btn-input changelog-search" list="sound-list" placeholder={locale('changelog.search')}
|
||||
value={search} onChange={setSearch} />
|
||||
<Btn icon={sort ? 'sort_desc' : 'sort_asc'} label={sort ? 'Newest first' : 'Oldest first'} onClick={() => setSort(!sort)} />
|
||||
</div>
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { DataModel, Path } from '@mcschema/core'
|
||||
import { getCurrentUrl } from 'preact-router'
|
||||
import { useEffect, useErrorBoundary, useRef, useState } from 'preact/hooks'
|
||||
import { getCurrentUrl, route } from 'preact-router'
|
||||
import { useEffect, useErrorBoundary, useState } from 'preact/hooks'
|
||||
import config from '../../config.json'
|
||||
import { Analytics } from '../Analytics'
|
||||
import { Ad, Btn, BtnInput, BtnMenu, ErrorPanel, HasPreview, Octicon, PreviewPanel, SourcePanel, Tree } from '../components'
|
||||
import { useModel } from '../hooks'
|
||||
import { locale } from '../Locales'
|
||||
import { Ad, Btn, BtnMenu, ErrorPanel, HasPreview, Octicon, PreviewPanel, SearchList, SourcePanel, TextInput, Tree } from '../components'
|
||||
import { useLocale, useProject, useTitle, useVersion } from '../contexts'
|
||||
import { useActiveTimeout, useModel } from '../hooks'
|
||||
import { getOutput } from '../schema/transformOutput'
|
||||
import type { BlockStateRegistry, VersionId } from '../services'
|
||||
import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel } from '../services'
|
||||
import { getGenerator, getSearchParams, message, setSeachParams } from '../Utils'
|
||||
|
||||
type GeneratorProps = {
|
||||
lang: string,
|
||||
changeTitle: (title: string, versions?: VersionId[]) => unknown,
|
||||
version: VersionId,
|
||||
changeVersion: (version: VersionId) => unknown,
|
||||
interface Props {
|
||||
default?: true,
|
||||
}
|
||||
export function Generator({ lang, changeTitle, version, changeVersion }: GeneratorProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
export function Generator({}: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { version, changeVersion } = useVersion()
|
||||
const { project, file, updateFile, openFile, closeFile } = useProject()
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [errorBoundary, errorRetry] = useErrorBoundary()
|
||||
if (errorBoundary) {
|
||||
@@ -34,7 +33,7 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat
|
||||
.filter(v => checkVersion(v.id, gen.minVersion, gen.maxVersion))
|
||||
.map(v => v.id as VersionId)
|
||||
|
||||
changeTitle(loc('title.generator', loc(gen.id)), allowedVersions)
|
||||
useTitle(locale('title.generator', locale(gen.id)), allowedVersions)
|
||||
|
||||
if (!checkVersion(version, gen.minVersion)) {
|
||||
setError(`The minimum version for this generator is ${gen.minVersion}`)
|
||||
@@ -70,11 +69,58 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat
|
||||
.catch(e => { console.error(e); setError(message(e)) })
|
||||
}, [version, gen.id])
|
||||
|
||||
const [dirty, setDirty] = useState(false)
|
||||
useModel(model, () => {
|
||||
setSeachParams({ version: undefined, preset: undefined })
|
||||
setError(null)
|
||||
setDirty(true)
|
||||
})
|
||||
|
||||
const [fileRename, setFileRename] = useState('')
|
||||
const [fileSaved, doSave] = useActiveTimeout()
|
||||
const [fileError, doFileError] = useActiveTimeout()
|
||||
|
||||
const doFileRename = () => {
|
||||
if (fileRename !== file?.id && fileRename && model && blockStates) {
|
||||
const data = getOutput(model, blockStates)
|
||||
const success = updateFile(gen.id, file?.id, { id: fileRename, data })
|
||||
if (success) {
|
||||
doSave()
|
||||
} else {
|
||||
doFileError()
|
||||
if (file) {
|
||||
setFileRename(file?.id)
|
||||
}
|
||||
}
|
||||
} else if (file) {
|
||||
setFileRename(file?.id)
|
||||
}
|
||||
}
|
||||
|
||||
const deleteFile = () => {
|
||||
if (file) {
|
||||
updateFile(gen.id, file.id, {})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (file) {
|
||||
setFileRename(file.id)
|
||||
}
|
||||
}, [file])
|
||||
|
||||
useEffect(() => {
|
||||
if (model) {
|
||||
setFileRename(file?.id ?? '')
|
||||
if (file && gen.id === file.type) {
|
||||
model.reset(DataModel.wrapLists(file.data))
|
||||
} else {
|
||||
model.reset(DataModel.wrapLists(model.schema.default()), true)
|
||||
}
|
||||
setDirty(false)
|
||||
}
|
||||
}, [file, model])
|
||||
|
||||
const reset = () => {
|
||||
Analytics.generatorEvent('reset')
|
||||
model?.reset(DataModel.wrapLists(model.schema.default()), true)
|
||||
@@ -99,28 +145,34 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat
|
||||
model?.redo()
|
||||
}
|
||||
}
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.ctrlKey && e.key === 's') {
|
||||
e.preventDefault()
|
||||
if (model && blockStates && file) {
|
||||
Analytics.generatorEvent('save', 'Hotkey')
|
||||
const data = getOutput(model, blockStates)
|
||||
updateFile(gen.id, file?.id, { id: file?.id, data })
|
||||
setDirty(false)
|
||||
doSave()
|
||||
}
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
document.addEventListener('keyup', onKeyUp)
|
||||
document.addEventListener('keydown', onKeyDown)
|
||||
return () => {
|
||||
document.removeEventListener('keyup', onKeyUp)
|
||||
document.removeEventListener('keydown', onKeyDown)
|
||||
}
|
||||
}, [model])
|
||||
}, [model, blockStates, file])
|
||||
|
||||
const [presetFilter, setPresetFilter] = useState('')
|
||||
const [presetResults, setPresetResults] = useState<string[]>([])
|
||||
const [presets, setPresets] = useState<string[]>([])
|
||||
useEffect(() => {
|
||||
getCollections(version)
|
||||
.then(collections => {
|
||||
const terms = (presetFilter ?? '').trim().split(' ')
|
||||
const presets = collections.get(gen.id)
|
||||
.map(p => p.slice(10))
|
||||
.filter(p => terms.every(t => p.includes(t)))
|
||||
if (presets) {
|
||||
setPresetResults(presets)
|
||||
}
|
||||
})
|
||||
getCollections(version).then(collections => {
|
||||
setPresets(collections.get(gen.id).map(p => p.slice(10)))
|
||||
})
|
||||
.catch(e => { console.error(e); setError(e.message) })
|
||||
}, [version, gen.id, presetFilter])
|
||||
}, [version, gen.id])
|
||||
|
||||
const selectPreset = (id: string) => {
|
||||
loadPreset(id).then(preset => {
|
||||
@@ -172,15 +224,7 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat
|
||||
setImport(0)
|
||||
}
|
||||
|
||||
const [copyActive, setCopyActive] = useState(false)
|
||||
const copyTimeout = useRef<number | undefined>(undefined)
|
||||
const copySuccess = () => {
|
||||
setCopyActive(true)
|
||||
if (copyTimeout.current !== undefined) clearTimeout(copyTimeout.current)
|
||||
copyTimeout.current = setTimeout(() => {
|
||||
setCopyActive(false)
|
||||
}, 2000) as any
|
||||
}
|
||||
const [copyActive, copySuccess] = useActiveTimeout()
|
||||
|
||||
const [previewShown, setPreviewShown] = useState(false)
|
||||
const hasPreview = HasPreview.includes(gen.id)
|
||||
@@ -201,47 +245,59 @@ export function Generator({ lang, changeTitle, version, changeVersion }: Generat
|
||||
<main class={previewShown ? 'has-preview' : ''}>
|
||||
<Ad id="data-pack-generator" type="text" />
|
||||
<div class="controls">
|
||||
<Btn icon="upload" label={loc('import')} onClick={importSource} />
|
||||
<BtnMenu icon="archive" label={loc('presets')} relative={false}>
|
||||
<BtnInput icon="search" large value={presetFilter} onChange={setPresetFilter} doSelect={1} placeholder={loc('search')} />
|
||||
<div class="result-list">
|
||||
{presetResults.map(preset => <Btn label={preset} onClick={() => selectPreset(preset)} />)}
|
||||
<div class={`project-controls ${file && 'has-file'}`}>
|
||||
<div class="btn-row">
|
||||
<BtnMenu icon="repo" label={project.name} relative={false}>
|
||||
<Btn icon="arrow_left" label={locale('project.go_to')} onClick={() => route('/project')} />
|
||||
{file && <Btn icon="file" label={locale('project.new_file')} onClick={closeFile} />}
|
||||
<SearchList searchPlaceholder={locale(project.name === 'Drafts' ? 'project.search_drafts' : 'project.search')} noResults={locale('project.no_files')} values={project.files.filter(f => f.type === gen.id).map(f => f.id)} onSelect={(id) => openFile(gen.id, id)} />
|
||||
</BtnMenu>
|
||||
<TextInput class="btn btn-input" placeholder={locale('project.unsaved_file')} value={fileRename} onChange={setFileRename} onEnter={doFileRename} onBlur={doFileRename} />
|
||||
{file && <Btn icon="trashcan" tooltip={locale('project.delete_file')} onClick={deleteFile} />}
|
||||
</div>
|
||||
{presetResults.length === 0 && <Btn label={loc('no_presets')}/>}
|
||||
</BtnMenu>
|
||||
<BtnMenu icon="tag" label={version} data-cy="version-switcher">
|
||||
{allowedVersions.reverse().map(v =>
|
||||
<Btn label={v} active={v === version} onClick={() => changeVersion(v)} />
|
||||
)}
|
||||
</BtnMenu>
|
||||
<BtnMenu icon="kebab_horizontal" tooltip={loc('more')}>
|
||||
<Btn icon="history" label={loc('reset')} onClick={reset} />
|
||||
<Btn icon="arrow_left" label={loc('undo')} onClick={undo} />
|
||||
<Btn icon="arrow_right" label={loc('redo')} onClick={redo} />
|
||||
</BtnMenu>
|
||||
{dirty ? <div class="status-icon">{Octicon.dot_fill}</div>
|
||||
: fileSaved ? <div class="status-icon active">{Octicon.check}</div>
|
||||
: fileError && <div class="status-icon danger">{Octicon.x}</div> }
|
||||
</div>
|
||||
<div class="generator-controls">
|
||||
<Btn icon="upload" label={locale('import')} onClick={importSource} />
|
||||
<BtnMenu icon="archive" label={locale('presets')} relative={false}>
|
||||
<SearchList searchPlaceholder={locale('search')} noResults={locale('no_presets')} values={presets} onSelect={selectPreset}/>
|
||||
</BtnMenu>
|
||||
<BtnMenu icon="tag" label={version} tooltip={locale('switch_version')} data-cy="version-switcher">
|
||||
{allowedVersions.reverse().map(v =>
|
||||
<Btn label={v} active={v === version} onClick={() => changeVersion(v)} />
|
||||
)}
|
||||
</BtnMenu>
|
||||
<BtnMenu icon="kebab_horizontal" tooltip={locale('more')}>
|
||||
<Btn icon="history" label={locale('reset')} onClick={reset} />
|
||||
<Btn icon="arrow_left" label={locale('undo')} onClick={undo} />
|
||||
<Btn icon="arrow_right" label={locale('redo')} onClick={redo} />
|
||||
</BtnMenu>
|
||||
</div>
|
||||
</div>
|
||||
{error && <ErrorPanel error={error} onDismiss={() => setError(null)} />}
|
||||
<Tree {...{lang, model, version, blockStates}} onError={setError} />
|
||||
<Tree {...{model, version, blockStates}} onError={setError} />
|
||||
</main>
|
||||
<div class="popup-actions" style={`--offset: -${8 + actionsShown * 50}px;`}>
|
||||
<div class={`popup-action action-preview${hasPreview ? ' shown' : ''} tooltipped tip-nw`} aria-label={loc(previewShown ? 'hide_preview' : 'show_preview')} onClick={togglePreview}>
|
||||
<div class={`popup-action action-preview${hasPreview ? ' shown' : ''} tooltipped tip-nw`} aria-label={locale(previewShown ? 'hide_preview' : 'show_preview')} onClick={togglePreview}>
|
||||
{previewShown ? Octicon.x_circle : Octicon.play}
|
||||
</div>
|
||||
<div class={`popup-action action-download${sourceShown ? ' shown' : ''} tooltipped tip-nw`} aria-label={loc('download')} onClick={downloadSource}>
|
||||
<div class={`popup-action action-download${sourceShown ? ' shown' : ''} tooltipped tip-nw`} aria-label={locale('download')} onClick={downloadSource}>
|
||||
{Octicon.download}
|
||||
</div>
|
||||
<div class={`popup-action action-copy${sourceShown ? ' shown' : ''}${copyActive ? ' active' : ''} tooltipped tip-nw`} aria-label={loc(copyActive ? 'copied' : 'copy')} onClick={copySource}>
|
||||
<div class={`popup-action action-copy${sourceShown ? ' shown' : ''}${copyActive ? ' active' : ''} tooltipped tip-nw`} aria-label={locale(copyActive ? 'copied' : 'copy')} onClick={copySource}>
|
||||
{copyActive ? Octicon.check : Octicon.clippy}
|
||||
</div>
|
||||
<div class={'popup-action action-code shown tooltipped tip-nw'} aria-label={loc(sourceShown ? 'hide_output' : 'show_output')} onClick={toggleSource}>
|
||||
<div class={'popup-action action-code shown tooltipped tip-nw'} aria-label={locale(sourceShown ? 'hide_output' : 'show_output')} onClick={toggleSource}>
|
||||
{sourceShown ? Octicon.chevron_right : Octicon.code}
|
||||
</div>
|
||||
</div>
|
||||
<div class={`popup-preview${previewShown ? ' shown' : ''}`}>
|
||||
<PreviewPanel {...{lang, model, version, id: gen.id}} shown={previewShown} onError={setError} />
|
||||
<PreviewPanel {...{model, version, id: gen.id}} shown={previewShown} onError={setError} />
|
||||
</div>
|
||||
<div class={`popup-source${sourceShown ? ' shown' : ''}`}>
|
||||
<SourcePanel {...{lang, model, blockStates, doCopy, doDownload, doImport}} name={gen.schema ?? 'data'} copySuccess={copySuccess} onError={setError} />
|
||||
<SourcePanel {...{model, blockStates, doCopy, doDownload, doImport}} name={gen.schema ?? 'data'} copySuccess={copySuccess} onError={setError} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
import config from '../../config.json'
|
||||
import { ToolCard } from '../components'
|
||||
import { locale } from '../Locales'
|
||||
import { useLocale, useTitle } from '../contexts'
|
||||
import { cleanUrl } from '../Utils'
|
||||
|
||||
type HomeProps = {
|
||||
lang: string,
|
||||
changeTitle: (title: string) => unknown,
|
||||
interface Props {
|
||||
path?: string,
|
||||
}
|
||||
export function Home({ lang, changeTitle }: HomeProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
changeTitle(loc('title.home'))
|
||||
export function Home({}: Props) {
|
||||
const { locale } = useLocale()
|
||||
useTitle(locale('title.home'))
|
||||
return <main>
|
||||
<div class="home">
|
||||
<ToolCard title="Data packs">
|
||||
{config.generators.filter(g => !g.category).map(g =>
|
||||
<ToolCard title={loc(g.id)} link={cleanUrl(g.url)} />
|
||||
<ToolCard title={locale(g.id)} link={cleanUrl(g.url)} />
|
||||
)}
|
||||
<ToolCard title={loc('worldgen')} link="/worldgen/" />
|
||||
<ToolCard title={locale('worldgen')} link="/worldgen/" />
|
||||
</ToolCard>
|
||||
<ToolCard title="Resource packs">
|
||||
{config.generators.filter(g => g.category === 'assets').map(g =>
|
||||
<ToolCard title={loc(g.id)} link={cleanUrl(g.url)} />
|
||||
<ToolCard title={locale(g.id)} link={cleanUrl(g.url)} />
|
||||
)}
|
||||
</ToolCard>
|
||||
<ToolCard title="Report Inspector" icon="report"
|
||||
|
||||
28
src/app/pages/Project.tsx
Normal file
28
src/app/pages/Project.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useMemo } from 'preact/hooks'
|
||||
import { Ad, TreeView } from '../components'
|
||||
import { getFilePath, useLocale, useProject, useTitle } from '../contexts'
|
||||
|
||||
interface Props {
|
||||
path?: string,
|
||||
}
|
||||
export function Project({}: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { project, openFile } = useProject()
|
||||
useTitle(locale('title.project', project.name))
|
||||
const entries = useMemo(() => project.files.map(getFilePath), project.files)
|
||||
|
||||
const selectFile = (entry: string) => {
|
||||
const [, namespace, type, ...id] = entry.split('/')
|
||||
openFile(type, `${namespace}:${id}`)
|
||||
}
|
||||
|
||||
return <main>
|
||||
<Ad id="data-pack-project" type="text" />
|
||||
<div class="project">
|
||||
<h2>{project.name}</h2>
|
||||
<div class="file-view">
|
||||
<TreeView entries={entries} onSelect={selectFile}/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
import { useEffect, useRef, useState } from 'preact/hooks'
|
||||
import config from '../../config.json'
|
||||
import { Ad, Btn, BtnMenu, ErrorPanel, SoundConfig, TextInput } from '../components'
|
||||
import { locale } from '../Locales'
|
||||
import { useLocale, useTitle, useVersion } from '../contexts'
|
||||
import type { SoundEvents, VersionAssets, VersionId } from '../services'
|
||||
import { getAssets, getSounds } from '../services'
|
||||
import { hexId, message } from '../Utils'
|
||||
|
||||
type SoundsProps = {
|
||||
interface Props {
|
||||
path?: string,
|
||||
lang: string,
|
||||
changeTitle: (title: string, versions?: VersionId[]) => unknown,
|
||||
version: VersionId,
|
||||
changeVersion: (version: VersionId) => unknown,
|
||||
}
|
||||
export function Sounds({ lang, changeTitle, version, changeVersion }: SoundsProps) {
|
||||
const loc = locale.bind(null, lang)
|
||||
export function Sounds({}: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { version, changeVersion } = useVersion()
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
changeTitle(loc('title.sounds'))
|
||||
useTitle(locale('title.sounds'))
|
||||
|
||||
const [assets, setAssets] = useState<VersionAssets>({})
|
||||
const [sounds, setSounds] = useState<SoundEvents>({})
|
||||
@@ -63,13 +60,13 @@ export function Sounds({ lang, changeTitle, version, changeVersion }: SoundsProp
|
||||
{soundKeys.length > 0 && <>
|
||||
<div class="controls sounds-controls">
|
||||
<div class="sound-search-group">
|
||||
<TextInput class="btn btn-input sound-search" list="sound-list" placeholder={loc('sounds.search')}
|
||||
<TextInput class="btn btn-input sound-search" list="sound-list" placeholder={locale('sounds.search')}
|
||||
value={search} onChange={setSearch} onEnter={addConfig} />
|
||||
<Btn icon="plus" tooltip={loc('sounds.add_sound')} class="add-sound" onClick={addConfig} />
|
||||
<Btn icon="plus" tooltip={locale('sounds.add_sound')} class="add-sound" onClick={addConfig} />
|
||||
</div>
|
||||
{configs.length > 1 && <Btn icon="play" label={ loc('sounds.play_all')} class="play-all-sounds" onClick={playAll} />}
|
||||
{configs.length > 1 && <Btn icon="play" label={ locale('sounds.play_all')} class="play-all-sounds" onClick={playAll} />}
|
||||
<div class="spacer"></div>
|
||||
<Btn icon="download" label={loc('download')} tooltip={loc('sounds.download_function')} class="download-sounds" onClick={downloadFunction} />
|
||||
<Btn icon="download" label={locale('download')} tooltip={locale('sounds.download_function')} class="download-sounds" onClick={downloadFunction} />
|
||||
<BtnMenu icon="tag" label={version}>
|
||||
{config.versions.reverse().map(v =>
|
||||
<Btn label={v.id} active={v.id === version} onClick={() => changeVersion(v.id as VersionId)} />
|
||||
@@ -77,7 +74,7 @@ export function Sounds({ lang, changeTitle, version, changeVersion }: SoundsProp
|
||||
</BtnMenu>
|
||||
</div>
|
||||
<div class="sounds">
|
||||
{configs.map(c => <SoundConfig key={c.id} {...c} {...{ lang, assets, sounds, delayedPlay }} onEdit={editConfig(c.id)} onDelete={deleteConfig(c.id)} />)}
|
||||
{configs.map(c => <SoundConfig key={c.id} {...c} {...{ assets, sounds, delayedPlay }} onEdit={editConfig(c.id)} onDelete={deleteConfig(c.id)} />)}
|
||||
</div>
|
||||
<a ref={download} style="display: none;"></a>
|
||||
</>}
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from './Category'
|
||||
export * from './Changelog'
|
||||
export * from './Generator'
|
||||
export * from './Home'
|
||||
export * from './Project'
|
||||
export * from './Sounds'
|
||||
|
||||
Reference in New Issue
Block a user